fix: [毕设课程demo]
This commit is contained in:
parent
8b8e5fa497
commit
53b536f2fc
|
@ -12,6 +12,7 @@ public class SecurityConstants {
|
|||
* 这些路径可以直接访问,不需要认证
|
||||
*/
|
||||
public static final List<String> WHITE_LIST = List.of(
|
||||
"/bs/**",
|
||||
"/api/common/**", //公共接口
|
||||
"/demo/**", // 测试接口
|
||||
"/api/products",
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
package com.guwan.backend.controller;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.guwan.backend.common.Result;
|
||||
import com.guwan.backend.pojo.dto.user.*;
|
||||
import com.guwan.backend.service.EmailService;
|
||||
import com.guwan.backend.service.UserService;
|
||||
import com.guwan.backend.util.RedisUtils;
|
||||
import com.guwan.backend.util.SmsUtils;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.thymeleaf.context.Context;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/bs/user")
|
||||
@RequiredArgsConstructor
|
||||
@Validated
|
||||
public class BSUserController {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final EmailService emailService;
|
||||
|
||||
private final RedisUtils redisUtils;
|
||||
|
||||
@PostMapping("/register")
|
||||
public Result<UserDTO> register(@RequestBody @Valid BSRegisterDTO request) {
|
||||
try {
|
||||
log.info("用户注册: {}", request);
|
||||
// return Result.success("注册成功", userService.register(request));
|
||||
} catch (IllegalArgumentException e) {
|
||||
// return Result.validateFailed(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("注册失败", e);
|
||||
// return Result.error("系统错误");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@PostMapping("/login")
|
||||
public Result<String> login(@RequestBody @Valid LoginDto request) {
|
||||
try {
|
||||
log.info("用户登录: {}", request.getUsername());
|
||||
return Result.success("登录成功", userService.login(request).getToken());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Result.unauthorized(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("登录失败", e);
|
||||
return Result.error("系统错误");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("/current")
|
||||
public Result<UserDTO> getCurrentUser() {
|
||||
UserDTO user = userService.getCurrentUser();
|
||||
if (user == null) {
|
||||
return Result.unauthorized("用户未登录");
|
||||
}
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public Result<UserDTO> getUserById(@PathVariable Long id) {
|
||||
UserDTO user = userService.getUserById(id);
|
||||
if (user == null) {
|
||||
return Result.notFound("用户不存在");
|
||||
}
|
||||
return Result.success(user);
|
||||
}
|
||||
|
||||
@PostMapping("/token/refresh")
|
||||
public Result<String> refreshToken(@RequestHeader(value = "Authorization", required = false) String token) {
|
||||
if (token == null || !token.startsWith("Bearer ")) {
|
||||
return Result.error("无效的token");
|
||||
}
|
||||
try {
|
||||
String newToken = userService.refreshToken(token.substring(7));
|
||||
return Result.success(newToken);
|
||||
} catch (Exception e) {
|
||||
log.error("刷新token失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@PostMapping("/getEmailCode")
|
||||
public Result getEmailCode(@RequestBody @Valid EmailDto emailDto) {
|
||||
|
||||
String email = emailDto.getEmail();
|
||||
|
||||
log.info("邮箱注册: {}", email);
|
||||
|
||||
if (!email.endsWith("@stumail.xsyu.edu.cn")){
|
||||
return Result.error("对不起!您不属于西安石油大学学生!!!");
|
||||
}
|
||||
|
||||
Context context = new Context();
|
||||
|
||||
context.setVariable("nowDate", DateUtil.now());
|
||||
|
||||
String code = RandomUtil.randomNumbers(6);
|
||||
|
||||
redisUtils.set(email, code, 10);
|
||||
|
||||
context.setVariable("code", code.toCharArray());
|
||||
|
||||
emailService.sendHtmlMessage(email,
|
||||
"C/C++在线学习平台邮箱验证码", "email_template.html", context);
|
||||
return Result.success("邮件验证码发送成功");
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/getPhoneCode")
|
||||
public Result registerByPhone(@RequestBody @Valid PhoneDto phoneDto) throws Exception {
|
||||
String phone = phoneDto.getPhone();
|
||||
|
||||
log.info("手机号注册: {}", phone);
|
||||
|
||||
String random = RandomUtil.randomNumbers(6);
|
||||
|
||||
SmsUtils.sendMessage(phone, random);
|
||||
|
||||
redisUtils.set(phone, random, 10);
|
||||
|
||||
return Result.success("手机验证码发送成功");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@PostMapping("/password/reset")
|
||||
public Result<Void> resetPassword(@RequestBody ChangePasswordDTO changePasswordDTO) {
|
||||
|
||||
log.debug("更改方式: {}, 内容: {}",
|
||||
changePasswordDTO.getChangeWay(),
|
||||
changePasswordDTO.getInfo());
|
||||
|
||||
try {
|
||||
userService.resetPassword(changePasswordDTO);
|
||||
return Result.success();
|
||||
} catch (Exception e) {
|
||||
log.error("重置密码失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@PutMapping("/info")
|
||||
public Result<UserDTO> updateUserInfo(@RequestBody @Valid UserDTO userDTO) {
|
||||
try {
|
||||
return Result.success(userService.updateUserInfo(userDTO));
|
||||
} catch (Exception e) {
|
||||
log.error("更新用户信息失败", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.guwan.backend.controller;
|
||||
|
||||
|
||||
|
||||
import com.guwan.backend.common.Result;
|
||||
import com.guwan.backend.mongodb.CategoryService;
|
||||
import com.guwan.backend.pojo.Courses;
|
||||
import com.guwan.backend.pojo.dto.BSCategory;
|
||||
import com.guwan.backend.service.BSCategoryService;
|
||||
import com.guwan.backend.service.CoursesService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 课程表(Courses)表控制层
|
||||
*
|
||||
* @author Guwan
|
||||
* @since 2025-03-13 22:47:31
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/bs/courses")
|
||||
@RequiredArgsConstructor
|
||||
public class CoursesController {
|
||||
/**
|
||||
* 服务对象
|
||||
*/
|
||||
|
||||
private final CoursesService coursesService;
|
||||
|
||||
private final BSCategoryService categoryService;
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
*
|
||||
* @param courses 筛选条件
|
||||
* @param pageRequest 分页对象
|
||||
* @return 查询结果
|
||||
*/
|
||||
@GetMapping
|
||||
public Result queryByPage() {
|
||||
// return Result.success(this.coursesService.queryByPage(courses, pageRequest));
|
||||
|
||||
return Result.success(this.coursesService.list().stream()
|
||||
.peek(course -> {
|
||||
course.setCategoryName(categoryService.list()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(BSCategory::getId, BSCategory::getName))
|
||||
.get(course.getCategoryId())); // 赋值类别名称
|
||||
})
|
||||
.toList());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过主键查询单条数据
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 单条数据
|
||||
*/
|
||||
@GetMapping("{id}")
|
||||
public Result<Courses> queryById(@PathVariable("id") Integer id) {
|
||||
return Result.success(this.coursesService.getById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增数据
|
||||
*
|
||||
* @param courses 实体
|
||||
* @return 新增结果
|
||||
*/
|
||||
@PostMapping
|
||||
public Result<Courses> add(Courses courses) {
|
||||
this.coursesService.save(courses);
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑数据
|
||||
*
|
||||
* @param courses 实体
|
||||
* @return 编辑结果
|
||||
*/
|
||||
@PutMapping
|
||||
public Result<Courses> edit(Courses courses) {
|
||||
//return Result.success(this.coursesService.update(courses));
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 删除是否成功
|
||||
*/
|
||||
@DeleteMapping
|
||||
public Result<Boolean> deleteById(Integer id) {
|
||||
return Result.success(this.coursesService.removeById(id));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
package com.guwan.backend.controller;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.arcsoft.face.FaceInfo;
|
||||
import com.arcsoft.face.enums.ExtractType;
|
||||
import com.arcsoft.face.toolkit.ImageFactory;
|
||||
import com.arcsoft.face.toolkit.ImageInfo;
|
||||
import com.guwan.backend.face.dto.FaceRecognitionResDTO;
|
||||
import com.guwan.backend.face.service.FaceEngineService;
|
||||
import com.guwan.config.GlobalValue;
|
||||
import com.guwan.config.MinioConfig;
|
||||
|
||||
|
||||
import com.guwan.util.UUIDUtil;
|
||||
import io.minio.MinioClient;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@Slf4j
|
||||
public class FaceController {
|
||||
|
||||
@Autowired
|
||||
private MinioConfig minioConfig;
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
@Autowired
|
||||
private GlobalValue globalValue;
|
||||
@Autowired
|
||||
private FaceEngineService faceEngineService;
|
||||
|
||||
|
||||
@GetMapping("/11")
|
||||
public Integer test() {
|
||||
try {
|
||||
|
||||
String tempOrgImageFile = "photo/t_1f952219ae6848a48fbf282d7d464623.jpg";
|
||||
// 调用statObject()来判断对象是否存在。如果不存在, statObject()抛出异常, 否则则代表对象存在。
|
||||
minioClient.statObject(minioConfig.getBucketName(), tempOrgImageFile);
|
||||
//判断人脸照片是否合格
|
||||
//1
|
||||
InputStream tempInputStream = minioClient.getObject(minioConfig.getBucketName(),
|
||||
tempOrgImageFile);
|
||||
//----------------算法检测----------------------------------------------
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int length;
|
||||
while ((length = tempInputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
byte[] bytes = outputStream.toByteArray();
|
||||
outputStream.close();
|
||||
tempInputStream.close();
|
||||
//-------------------------------------------------------------------------
|
||||
ImageInfo rgbData = ImageFactory.getRGBData(bytes);
|
||||
List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
|
||||
if (CollectionUtil.isNotEmpty(faceInfoList)) {
|
||||
FaceInfo faceInfo = faceInfoList.get(0);
|
||||
FaceRecognitionResDTO faceRecognitionResDTO = new FaceRecognitionResDTO();
|
||||
faceRecognitionResDTO.setRect(faceInfo.getRect());
|
||||
byte[] featureBytes = faceEngineService.extractFaceFeature(rgbData, faceInfo, ExtractType.REGISTER);
|
||||
if (featureBytes != null) {
|
||||
// UserRamCache.UserInfo userInfo = new UserCompareInfo();
|
||||
// userInfo.setFaceId(faceAddReqDTO.getName());
|
||||
// userInfo.setName(faceAddReqDTO.getName());
|
||||
// userInfo.setFaceFeature(feature);
|
||||
// //这边注册到内存缓存中,也可以根据业务,注册到数据库中
|
||||
// UserRamCache.addUser(userInfo);
|
||||
//entity.setFeature(Base64.getEncoder().encodeToString(featureBytes));
|
||||
System.out.println(Base64.getEncoder().encodeToString(featureBytes));
|
||||
}else{
|
||||
log.error("图片不合格,未检测到人脸");
|
||||
return 2;
|
||||
}
|
||||
}else{
|
||||
log.error("图片不合格,未检测到人脸");
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
String orgImageFileName = "face/"+ "o_" + UUIDUtil.uuid() + ".jpg";
|
||||
//拷贝临时文件正式文件
|
||||
minioClient.copyObject(
|
||||
minioConfig.getBucketName(),
|
||||
orgImageFileName,
|
||||
null,
|
||||
null,
|
||||
minioConfig.getBucketName(),
|
||||
tempOrgImageFile,
|
||||
null,
|
||||
null);
|
||||
|
||||
|
||||
|
||||
//删除临时文件oss
|
||||
minioClient.removeObject(minioConfig.getBucketName(), tempOrgImageFile);
|
||||
//删除本地临时文件
|
||||
// new File(tempFaceFilePath).delete();
|
||||
} catch (Exception e) {
|
||||
|
||||
e.printStackTrace();
|
||||
return 10;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,120 @@
|
|||
package com.guwan.backend.face;/*
|
||||
package com.guwan.face;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.guwan.config.GlobalValue;
|
||||
import com.guwan.face.entity.UserCompareInfo;
|
||||
import com.guwan.face.service.FaceEngineService;
|
||||
import com.guwan.face.util.Base64Util;
|
||||
import com.guwan.face.util.UserInfo;
|
||||
import com.guwan.face.util.UserRamGroup;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import net.shapelight.modules.ten.entity.TenCellEntity;
|
||||
import net.shapelight.modules.ten.entity.TenPersonEntity;
|
||||
import net.shapelight.modules.ten.service.TenCellService;
|
||||
import net.shapelight.modules.ten.service.TenPersonService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Order(1)
|
||||
@Slf4j
|
||||
public class FaceEngineAutoRun implements ApplicationRunner {
|
||||
@Autowired
|
||||
private FaceEngineService faceEngineService;
|
||||
@Autowired
|
||||
private TenPersonService tenPersonService;
|
||||
@Autowired
|
||||
private TenCellService tenCellService;
|
||||
@Autowired
|
||||
private GlobalValue globalValue;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) throws Exception {
|
||||
// 任务初始化
|
||||
log.debug("服务启动。。。。。初始化人脸库");
|
||||
// Map<String, String> fileMap = Maps.newHashMap();
|
||||
// fileMap.put("zhao1", "赵丽颖");
|
||||
// fileMap.put("yang1", "杨紫");
|
||||
// fileMap.put("baixue", "白雪");
|
||||
// fileMap.put("chenchuang", "陈创");
|
||||
// for (String f : fileMap.keySet()) {
|
||||
// ClassPathResource resource = new ClassPathResource("static/images/" + f + ".jpg");
|
||||
// InputStream inputStream = resource.getInputStream();
|
||||
// ImageInfo rgbData = ImageFactory.getRGBData(inputStream);
|
||||
// List<FaceInfo> faceInfoList = faceEngineService.detectFaces(rgbData);
|
||||
// if (CollectionUtil.isNotEmpty(faceInfoList)) {
|
||||
// byte[] feature = faceEngineService.extractFaceFeature(rgbData, faceInfoList.get(0), ExtractType.REGISTER);
|
||||
// UserRamCache.UserInfo userInfo = new UserCompareInfo();
|
||||
// userInfo.setFaceId(f);
|
||||
// userInfo.setName(fileMap.get(f));
|
||||
// userInfo.setFaceFeature(feature);
|
||||
// //这边注册到内存缓存中,也可以根据业务,注册到数据库中
|
||||
// UserRamCache.addUser(userInfo);
|
||||
// }
|
||||
// }
|
||||
|
||||
// int count = tenPersonService.findCount();
|
||||
// int pageSize = 1000;
|
||||
// int page = count/pageSize;
|
||||
// if(count%1000!=0){
|
||||
// page = page+1;
|
||||
// }
|
||||
// int faceCount = 0;
|
||||
// for (int i = 0; i < page; i++) {
|
||||
// int start = i*1000;
|
||||
// List<TenPersonEntity> listPage = tenPersonService.listPage(start,1000);
|
||||
// for(TenPersonEntity personEntity: listPage){
|
||||
// if(personEntity.getFeature()!=null && personEntity.getFeature().length()>0){
|
||||
// UserRamCache.UserInfo userInfo = new UserCompareInfo();
|
||||
// userInfo.setFaceId(personEntity.getPersonId()+"");
|
||||
// userInfo.setName(personEntity.getName());
|
||||
// userInfo.setFaceFeature(Base64Util.base64ToBytes(personEntity.getFeature()));
|
||||
// //这边注册到内存缓存中
|
||||
// UserRamCache.addUser(userInfo);
|
||||
// faceCount++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
List<TenCellEntity> cellList = tenCellService.list(new QueryWrapper<TenCellEntity>()
|
||||
.eq("tenant_id",globalValue.getTenantId())
|
||||
.eq("delete_flag",0));
|
||||
for(TenCellEntity cellEntity: cellList){
|
||||
String cellId = cellEntity.getCellId()+"";
|
||||
UserRamGroup.addCell(cellId);
|
||||
UserRamGroup.addOrgId(cellEntity.getOrgId(),cellId);
|
||||
int count = tenPersonService.findCount(cellId);
|
||||
int pageSize = 1000;
|
||||
int page = count/pageSize;
|
||||
if(count%1000!=0){
|
||||
page = page+1;
|
||||
}
|
||||
int faceCount = 0;
|
||||
for (int i = 0; i < page; i++) {
|
||||
int start = i*1000;
|
||||
List<TenPersonEntity> listPage = tenPersonService.listPage(start,1000, cellId);
|
||||
for(TenPersonEntity personEntity: listPage){
|
||||
if(personEntity.getFeature()!=null && personEntity.getFeature().length()>0){
|
||||
UserInfo userInfo = new UserCompareInfo();
|
||||
userInfo.setFaceId(personEntity.getPersonId()+"");
|
||||
userInfo.setName(personEntity.getName());
|
||||
userInfo.setFaceFeature(Base64Util.base64ToBytes(personEntity.getFeature()));
|
||||
//这边注册到内存缓存中
|
||||
UserRamGroup.addUser(userInfo,cellId);
|
||||
faceCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.debug(cellEntity.getName()+":初始化人脸库完成,共 "+faceCount+" 人");
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,133 @@
|
|||
package com.guwan.backend.face.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class ArcFaceAutoConfiguration implements InitializingBean, DisposableBean {
|
||||
|
||||
|
||||
private static final String PLATFORM;
|
||||
|
||||
private static final String USER_HOME;
|
||||
|
||||
public static String CACHE_LIB_FOLDER;
|
||||
|
||||
@Value("${config.arcface-sdk.version}")
|
||||
public String ARC_FACE_VERSION;
|
||||
|
||||
static {
|
||||
String jvmName = System.getProperty("java.vm.name", "").toLowerCase();
|
||||
String osName = System.getProperty("os.name", "").toLowerCase();
|
||||
String osArch = System.getProperty("os.arch", "").toLowerCase();
|
||||
String abiType = System.getProperty("sun.arch.abi", "").toLowerCase();
|
||||
String libPath = System.getProperty("sun.boot.library.path", "").toLowerCase();
|
||||
USER_HOME = System.getProperty("user.home");
|
||||
if (jvmName.startsWith("dalvik") && osName.startsWith("linux")) {
|
||||
osName = "android";
|
||||
} else if (jvmName.startsWith("robovm") && osName.startsWith("darwin")) {
|
||||
osName = "ios";
|
||||
osArch = "arm";
|
||||
} else if (osName.startsWith("mac os x") || osName.startsWith("darwin")) {
|
||||
osName = "macosx";
|
||||
} else {
|
||||
int spaceIndex = osName.indexOf(' ');
|
||||
if (spaceIndex > 0) {
|
||||
osName = osName.substring(0, spaceIndex);
|
||||
}
|
||||
}
|
||||
if (osArch.equals("i386") || osArch.equals("i486") || osArch.equals("i586") || osArch.equals("i686")) {
|
||||
osArch = "x86";
|
||||
} else if (osArch.equals("amd64") || osArch.equals("x86-64") || osArch.equals("x64")) {
|
||||
osArch = "x86_64";
|
||||
} else if (osArch.startsWith("aarch64") || osArch.startsWith("armv8") || osArch.startsWith("arm64")) {
|
||||
osArch = "arm64";
|
||||
} else if ((osArch.startsWith("arm")) && ((abiType.equals("gnueabihf")) || (libPath.contains("openjdk-armhf")))) {
|
||||
osArch = "armhf";
|
||||
} else if (osArch.startsWith("arm")) {
|
||||
osArch = "arm";
|
||||
}
|
||||
PLATFORM = osName + "-" + osArch;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws IOException {
|
||||
CACHE_LIB_FOLDER = USER_HOME + "/.arcface/cache/" + ARC_FACE_VERSION + "/" + PLATFORM + "/";
|
||||
loadLibrary();
|
||||
}
|
||||
|
||||
public void loadLibrary() throws IOException {
|
||||
String baseFolder = "";
|
||||
String suffix = ".dll";
|
||||
if ("windows-x86_64".equals(PLATFORM)) {
|
||||
baseFolder = "WIN64";
|
||||
} else if ("windows-x86".equals(PLATFORM)) {
|
||||
baseFolder = "WIN32";
|
||||
} else if ("linux-x86_64".equals(PLATFORM)) {
|
||||
baseFolder = "LINUX64";
|
||||
suffix = ".so";
|
||||
}
|
||||
|
||||
if ("".equals(baseFolder)) {
|
||||
throw new RuntimeException("ArcFace不支持该操作系统");
|
||||
}
|
||||
|
||||
|
||||
File file = new File(CACHE_LIB_FOLDER);
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
|
||||
List<String> libList = new LinkedList<>();
|
||||
libList.add("libarcsoft_face");
|
||||
libList.add("libarcsoft_face_engine");
|
||||
libList.add("libarcsoft_face_engine_jni");
|
||||
|
||||
for (String lib : libList) {
|
||||
ClassPathResource resource = new ClassPathResource("libs/" + ARC_FACE_VERSION + "/" + baseFolder + "/" + lib + suffix);
|
||||
InputStream inputStream = resource.getInputStream();
|
||||
int faceLength = inputStream.available();
|
||||
File facePath = new File(CACHE_LIB_FOLDER + lib + suffix);
|
||||
if (facePath.exists()) {
|
||||
if (facePath.length() == faceLength) {
|
||||
continue;
|
||||
}
|
||||
facePath.delete();
|
||||
}
|
||||
writeToLocal(CACHE_LIB_FOLDER + lib + suffix, inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeToLocal(String destination, InputStream input)
|
||||
throws IOException {
|
||||
int index;
|
||||
byte[] bytes = new byte[1024 * 100];
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(destination);
|
||||
while ((index = input.read(bytes)) != -1) {
|
||||
fileOutputStream.write(bytes, 0, index);
|
||||
}
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
input.close();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author shentao
|
||||
* @desc
|
||||
* @date 2022/3/30
|
||||
*/
|
||||
@Data
|
||||
public class CompareFacesReqDTO {
|
||||
private String image1;
|
||||
private String image2;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FaceAddReqDTO {
|
||||
|
||||
private String name;
|
||||
|
||||
private String image;
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FaceDetectReqDTO {
|
||||
|
||||
private String image;
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
import com.arcsoft.face.Rect;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FaceDetectResDTO {
|
||||
private Rect rect;
|
||||
private int orient;
|
||||
private int faceId = -1;
|
||||
private int age = -1;
|
||||
private int gender = -1;
|
||||
private int liveness = -1;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FaceRecognitionReqDTO {
|
||||
|
||||
private String image;
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
|
||||
import com.arcsoft.face.Rect;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class FaceRecognitionResDTO {
|
||||
|
||||
private Rect rect;
|
||||
private String personId;
|
||||
private String name;
|
||||
private float similar;
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class FaceVideoVo {
|
||||
/**
|
||||
* 人员id
|
||||
*/
|
||||
private String personId;
|
||||
/**
|
||||
* 人员name
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 是否正脸
|
||||
*/
|
||||
|
||||
private Integer isHeadOnView; //0否1是
|
||||
|
||||
/**
|
||||
* 图片url
|
||||
*/
|
||||
private String imageUrl;
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.guwan.backend.face.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class GetFaceListResDTO {
|
||||
|
||||
private String id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String url;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.guwan.backend.face.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ProcessInfo {
|
||||
private int age;
|
||||
private int gender;
|
||||
private int liveness;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.guwan.backend.face.entity;
|
||||
|
||||
|
||||
import com.guwan.face.util.UserInfo;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
@Data
|
||||
public class UserCompareInfo extends UserInfo {
|
||||
private Float similar;
|
||||
private Integer isHeadOnView; //0否1是
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package com.guwan.backend.face.enums;
|
||||
|
||||
|
||||
import com.guwan.face.rpc.ErrorCode;
|
||||
import lombok.Getter;
|
||||
|
||||
|
||||
@Getter
|
||||
public enum ErrorCodeEnum implements ErrorCode {
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS(0, "success", "成功"),
|
||||
FAIL(1, "fail", "失败"),
|
||||
PARAM_ERROR(2, "param error", "参数错误"),
|
||||
SYSTEM_ERROR(999, "system error", "系统错误"),
|
||||
|
||||
;
|
||||
private Integer code;
|
||||
private String desc;
|
||||
private String descCN;
|
||||
|
||||
ErrorCodeEnum(Integer code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
ErrorCodeEnum(Integer code, String desc, String descCN) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
this.descCN = descCN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescCN() {
|
||||
return descCN;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.guwan.backend.face.face;
|
||||
|
||||
import com.arcsoft.face.FaceInfo;
|
||||
|
||||
public class FacePreviewInfo {
|
||||
private FaceInfo faceInfo;
|
||||
private int trackId;
|
||||
private int gender = -1;
|
||||
|
||||
public FacePreviewInfo(FaceInfo faceInfo, int trackId) {
|
||||
this.faceInfo = faceInfo;
|
||||
this.trackId = trackId;
|
||||
}
|
||||
|
||||
public FacePreviewInfo(FaceInfo faceInfo, int trackId, int gender) {
|
||||
this.faceInfo = faceInfo;
|
||||
this.trackId = trackId;
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public int getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public void setGender(int gender) {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
public FaceInfo getFaceInfo() {
|
||||
return faceInfo;
|
||||
}
|
||||
|
||||
public void setFaceInfo(FaceInfo faceInfo) {
|
||||
this.faceInfo = faceInfo;
|
||||
}
|
||||
|
||||
|
||||
public int getTrackId() {
|
||||
return trackId;
|
||||
}
|
||||
|
||||
public void setTrackId(int trackId) {
|
||||
this.trackId = trackId;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
package com.guwan.backend.face.face;
|
||||
|
||||
import com.arcsoft.face.*;
|
||||
import com.arcsoft.face.enums.DetectMode;
|
||||
import com.arcsoft.face.enums.ErrorInfo;
|
||||
import com.arcsoft.face.enums.ExtractType;
|
||||
import com.arcsoft.face.toolkit.ImageFactory;
|
||||
import com.arcsoft.face.toolkit.ImageInfo;
|
||||
import com.guwan.face.config.ArcFaceAutoConfiguration;
|
||||
import com.guwan.face.factory.FaceEngineFactory;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Slf4j
|
||||
public final class FaceRecognize {
|
||||
|
||||
/**
|
||||
* VIDEO模式人脸检测引擎,用于预览帧人脸追踪
|
||||
*/
|
||||
private static FaceEngine ftEngine;
|
||||
|
||||
/**
|
||||
* 人脸注册引擎
|
||||
*/
|
||||
private static FaceEngine regEngine;
|
||||
|
||||
/**
|
||||
* 用于人脸识别的引擎池
|
||||
*/
|
||||
private static GenericObjectPool<FaceEngine> frEnginePool;
|
||||
|
||||
|
||||
private static volatile ConcurrentHashMap<Integer, FaceResult> faceResultRegistry = new ConcurrentHashMap<>();
|
||||
|
||||
private static ExecutorService frService = Executors.newFixedThreadPool(20);
|
||||
|
||||
public static ConcurrentHashMap<String, byte[]> faceFeatureRegistry = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 初始化引擎
|
||||
*/
|
||||
public void initEngine(String appId, String sdkKey, String activeKey, String activeFile) {
|
||||
|
||||
//引擎配置
|
||||
ftEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER);
|
||||
int activeCode;
|
||||
if (StringUtils.isNotEmpty(activeFile)) {
|
||||
activeCode = ftEngine.activeOffline(activeFile);
|
||||
} else {
|
||||
activeCode = ftEngine.activeOnline(appId, sdkKey, activeKey);
|
||||
}
|
||||
|
||||
EngineConfiguration ftEngineCfg = new EngineConfiguration();
|
||||
ftEngineCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_VIDEO);
|
||||
ftEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceDetect(true).build());
|
||||
int ftInitCode = ftEngine.init(ftEngineCfg);
|
||||
|
||||
//引擎配置
|
||||
regEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER);
|
||||
|
||||
EngineConfiguration regEngineCfg = new EngineConfiguration();
|
||||
regEngineCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
|
||||
regEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceDetect(true).supportFaceRecognition(true).build());
|
||||
int regInitCode = regEngine.init(regEngineCfg);
|
||||
|
||||
|
||||
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
||||
poolConfig.setMaxIdle(5);
|
||||
poolConfig.setMaxTotal(5);
|
||||
poolConfig.setMinIdle(5);
|
||||
poolConfig.setLifo(false);
|
||||
EngineConfiguration frEngineCfg = new EngineConfiguration();
|
||||
frEngineCfg.setFunctionConfiguration(FunctionConfiguration.builder().supportFaceRecognition(true).build());
|
||||
frEnginePool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey,activeFile, frEngineCfg), poolConfig);//底层库算法对象池
|
||||
|
||||
|
||||
if (!(activeCode == ErrorInfo.MOK.getValue() || activeCode == ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue())) {
|
||||
log.error("activeCode: " + activeCode);
|
||||
throw new RuntimeException("activeCode: " + activeCode);
|
||||
}
|
||||
if (ftInitCode != ErrorInfo.MOK.getValue()) {
|
||||
log.error("ftInitEngine: " + ftInitCode);
|
||||
throw new RuntimeException("ftInitEngine: " + ftInitCode);
|
||||
}
|
||||
|
||||
if (regInitCode != ErrorInfo.MOK.getValue()) {
|
||||
log.error("regInitEngine: " + regInitCode);
|
||||
throw new RuntimeException("regInitEngine: " + regInitCode);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static void registerFace(String imagePath) {
|
||||
|
||||
log.info("正在注册人脸");
|
||||
|
||||
int count = 0;
|
||||
if (regEngine != null) {
|
||||
File file = new File(imagePath);
|
||||
File[] files = file.listFiles();
|
||||
|
||||
for (File file1 : files) {
|
||||
ImageInfo imageInfo = ImageFactory.getRGBData(file1);
|
||||
if (imageInfo != null) {
|
||||
List<FaceInfo> faceInfoList = new ArrayList<>();
|
||||
int code = regEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
|
||||
imageInfo.getImageFormat(), faceInfoList);
|
||||
|
||||
if (code == 0 && faceInfoList.size() > 0) {
|
||||
FaceFeature faceFeature = new FaceFeature();
|
||||
int resCode = regEngine.extractFaceFeature(imageInfo, faceInfoList.get(0), ExtractType.REGISTER, 0, faceFeature);
|
||||
if (resCode == 0) {
|
||||
int lastIndexOf = file1.getName().lastIndexOf(".");
|
||||
String name = file1.getName().substring(0, file1.getName().length() - lastIndexOf - 1);
|
||||
faceFeatureRegistry.put(name, faceFeature.getFeatureData());
|
||||
log.info("成功注册人脸:" + name);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("人脸注册完成,共注册:" + count + "张人脸");
|
||||
} else {
|
||||
throw new RuntimeException("注册失败,引擎未初始化或初始化失败");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void registerFace(Map<String, byte[]> face) {
|
||||
face.forEach((k, v) -> {
|
||||
faceFeatureRegistry.put(k, v.clone());
|
||||
});
|
||||
}
|
||||
|
||||
public static void removeFace(String name) {
|
||||
faceFeatureRegistry.remove(name);
|
||||
}
|
||||
|
||||
public static void clearFace() {
|
||||
faceFeatureRegistry.clear();
|
||||
}
|
||||
|
||||
public static FaceResult getFaceResult(FaceInfo faceInfo, ImageInfo imageInfo) {
|
||||
FaceResult faceResult = faceResultRegistry.get(faceInfo.getFaceId());
|
||||
if (faceResult == null) {
|
||||
faceResult = new FaceResult();
|
||||
faceResultRegistry.put(faceInfo.getFaceId(), faceResult);
|
||||
frService.submit(new FaceInfoRunnable(faceInfo, imageInfo, faceResult));
|
||||
} else if (faceResult.isFlag()) {
|
||||
return faceResult;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<FacePreviewInfo> detectFaces(ImageInfo imageInfo) {
|
||||
if (ftEngine != null) {
|
||||
List<FaceInfo> faceInfoList = new ArrayList<>();
|
||||
int code = ftEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(),
|
||||
imageInfo.getImageFormat(), faceInfoList);
|
||||
|
||||
List<FacePreviewInfo> previewInfoList = new LinkedList<>();
|
||||
for (FaceInfo faceInfo : faceInfoList) {
|
||||
FacePreviewInfo facePreviewInfo = new FacePreviewInfo();
|
||||
facePreviewInfo.setFaceInfo(faceInfo);
|
||||
previewInfoList.add(facePreviewInfo);
|
||||
}
|
||||
|
||||
clearFaceResultRegistry(faceInfoList);
|
||||
return previewInfoList;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private long lastClearTime = System.currentTimeMillis();
|
||||
|
||||
//清理过时的人脸
|
||||
private void clearFaceResultRegistry(List<FaceInfo> faceInfoList) {
|
||||
if (System.currentTimeMillis() - lastClearTime > 5000) {
|
||||
Iterator<Integer> iterator = faceResultRegistry.keySet().iterator();
|
||||
for (; iterator.hasNext(); ) {
|
||||
Integer next = iterator.next();
|
||||
boolean flag = false;
|
||||
for (FaceInfo faceInfo : faceInfoList) {
|
||||
if (next.equals(faceInfo.getFaceId())) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
}
|
||||
lastClearTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
public static class FaceResult {
|
||||
private boolean flag = false;
|
||||
private String name;
|
||||
private float score;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public class FacePreviewInfo {
|
||||
|
||||
private FaceInfo faceInfo;
|
||||
private int age;
|
||||
private boolean liveness;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static class FaceInfoRunnable implements Runnable {
|
||||
private FaceInfo faceInfo;
|
||||
private ImageInfo imageInfo;
|
||||
private FaceResult faceResult;
|
||||
|
||||
public FaceInfoRunnable(FaceInfo faceInfo, ImageInfo imageInfo, FaceResult faceResult) {
|
||||
this.faceInfo = faceInfo;
|
||||
this.imageInfo = imageInfo;
|
||||
this.faceResult = faceResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
FaceEngine frEngine = null;
|
||||
try {
|
||||
frEngine = frEnginePool.borrowObject();
|
||||
if (frEngine != null) {
|
||||
FaceFeature faceFeature = new FaceFeature();
|
||||
int resCode = frEngine.extractFaceFeature(imageInfo, faceInfo, ExtractType.RECOGNIZE, 0, faceFeature);
|
||||
if (resCode == 0) {
|
||||
|
||||
float score = 0.0F;
|
||||
Iterator<Map.Entry<String, byte[]>> iterator = faceFeatureRegistry.entrySet().iterator();
|
||||
for (; iterator.hasNext(); ) {
|
||||
Map.Entry<String, byte[]> next = iterator.next();
|
||||
FaceFeature faceFeatureTarget = new FaceFeature();
|
||||
faceFeatureTarget.setFeatureData(next.getValue());
|
||||
|
||||
FaceSimilar faceSimilar = new FaceSimilar();
|
||||
frEngine.compareFaceFeature(faceFeatureTarget, faceFeature, faceSimilar);
|
||||
if (faceSimilar.getScore() > score) {
|
||||
score = faceSimilar.getScore();
|
||||
faceResult.setName(next.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
log.info("相似度:" + score);
|
||||
if (score >= 0.8f) {
|
||||
faceResult.setScore(score);
|
||||
faceResult.setFlag(true);
|
||||
faceResultRegistry.put(faceInfo.getFaceId(), faceResult);
|
||||
} else {
|
||||
faceResultRegistry.remove(faceInfo.getFaceId());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
} finally {
|
||||
if (frEngine != null) {
|
||||
frEnginePool.returnObject(frEngine);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.guwan.backend.face.factory;
|
||||
|
||||
import com.arcsoft.face.EngineConfiguration;
|
||||
import com.arcsoft.face.FaceEngine;
|
||||
import com.arcsoft.face.enums.ErrorInfo;
|
||||
import com.guwan.face.config.ArcFaceAutoConfiguration;
|
||||
import com.guwan.face.enums.ErrorCodeEnum;
|
||||
import com.guwan.face.rpc.BusinessException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.pool2.BasePooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
|
||||
@Slf4j
|
||||
public class FaceEngineFactory extends BasePooledObjectFactory<FaceEngine> {
|
||||
|
||||
private String appId;
|
||||
private String sdkKey;
|
||||
private String activeKey;
|
||||
private String activeFile;
|
||||
private EngineConfiguration engineConfiguration;
|
||||
|
||||
|
||||
public FaceEngineFactory(String appId, String sdkKey, String activeKey, String activeFile, EngineConfiguration engineConfiguration) {
|
||||
this.appId = appId;
|
||||
this.sdkKey = sdkKey;
|
||||
this.activeKey = activeKey;
|
||||
this.activeFile=activeFile;
|
||||
this.engineConfiguration = engineConfiguration;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FaceEngine create() {
|
||||
|
||||
|
||||
FaceEngine faceEngine = new FaceEngine(ArcFaceAutoConfiguration.CACHE_LIB_FOLDER);
|
||||
|
||||
// FaceEngine faceEngine = new FaceEngine("/home/huangyifang/gb/咸阳师范/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64/");
|
||||
int activeCode;
|
||||
if (StringUtils.isNotEmpty(activeFile)) {
|
||||
activeCode = faceEngine.activeOffline(activeFile);
|
||||
} else {
|
||||
activeCode = faceEngine.activeOnline(appId, sdkKey, activeKey);
|
||||
}
|
||||
log.debug("引擎激活errorCode:" + activeCode);
|
||||
if (activeCode != ErrorInfo.MOK.getValue() && activeCode != ErrorInfo.MERR_ASF_ALREADY_ACTIVATED.getValue()) {
|
||||
log.error("引擎激活失败" + activeCode);
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "引擎激活失败" + activeCode);
|
||||
}
|
||||
int initCode = faceEngine.init(engineConfiguration);
|
||||
if (initCode != ErrorInfo.MOK.getValue()) {
|
||||
log.error("引擎初始化失败" + initCode);
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "引擎初始化失败" + initCode);
|
||||
}
|
||||
log.debug("初始化引擎errorCode:" + initCode);
|
||||
return faceEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<FaceEngine> wrap(FaceEngine faceEngine) {
|
||||
return new DefaultPooledObject<>(faceEngine);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject<FaceEngine> p) throws Exception {
|
||||
FaceEngine faceEngine = p.getObject();
|
||||
int result = faceEngine.unInit();
|
||||
super.destroyObject(p);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.guwan.backend.face.rpc;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @Author: st7251
|
||||
* @Date: 2018/11/23 14:18
|
||||
*/
|
||||
@Data
|
||||
public class BusinessException extends RuntimeException {
|
||||
private ErrorCode errorCode;
|
||||
private String msg;
|
||||
private String msgCN;
|
||||
|
||||
public BusinessException(Response response) {
|
||||
this.errorCode = new ErrorCode() {
|
||||
@Override
|
||||
public Integer getCode() {
|
||||
return response.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDesc() {
|
||||
return response.getMsg();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescCN() {
|
||||
return response.getMsg();
|
||||
}
|
||||
};
|
||||
this.msg=response.getMsg();
|
||||
this.msgCN=response.getMsg();
|
||||
}
|
||||
|
||||
public BusinessException(ErrorCode errorCode) {
|
||||
super(errorCode.getDesc());
|
||||
this.errorCode = errorCode;
|
||||
this.msg= errorCode.getDesc();
|
||||
this.msgCN=errorCode.getDescCN();
|
||||
}
|
||||
|
||||
public BusinessException(ErrorCode errorCode, String msg) {
|
||||
super(errorCode.getDesc());
|
||||
this.errorCode = errorCode;
|
||||
this.msg = msg;
|
||||
this.msgCN=msg;
|
||||
}
|
||||
|
||||
public BusinessException(Throwable cause, ErrorCode errorCode) {
|
||||
super(cause);
|
||||
this.errorCode = errorCode;
|
||||
this.msg= errorCode.getDesc();
|
||||
this.msgCN=errorCode.getDescCN();
|
||||
}
|
||||
|
||||
|
||||
public BusinessException(Throwable cause, ErrorCode errorCode, String msg) {
|
||||
super(cause);
|
||||
this.errorCode = errorCode;
|
||||
this.msg = msg;
|
||||
this.msgCN=msg;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package com.guwan.backend.face.rpc;
|
||||
|
||||
public interface ErrorCode {
|
||||
|
||||
|
||||
Integer getCode();
|
||||
|
||||
String getDesc();
|
||||
|
||||
String getDescCN();
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.guwan.backend.face.rpc;
|
||||
|
||||
|
||||
import com.guwan.face.enums.ErrorCodeEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
@Slf4j
|
||||
public class GlobalExceptionHandler{
|
||||
|
||||
|
||||
/**
|
||||
* 自定义异常
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Response businessException(BusinessException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
Response response = new Response();
|
||||
response.setCode(e.getErrorCode().getCode());
|
||||
response.setMsg(e.getMsgCN());
|
||||
return response;
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public Response handleIllegalArgumentException(IllegalArgumentException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
Response response = new Response();
|
||||
response.setCode(ErrorCodeEnum.PARAM_ERROR.getCode());
|
||||
response.setMsg(e.getMessage());
|
||||
return response;
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Response handleException(Exception e) {
|
||||
log.error(e.getMessage(), e);
|
||||
Response response = new Response();
|
||||
response.setCode(ErrorCodeEnum.SYSTEM_ERROR.getCode());
|
||||
response.setMsg(ErrorCodeEnum.SYSTEM_ERROR.getDescCN());
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.guwan.backend.face.rpc;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Response<T> {
|
||||
|
||||
private int code = -1;
|
||||
private String msg = "success";
|
||||
private T data;
|
||||
|
||||
public static <T> Response<T> newSuccessResponse(T data) {
|
||||
return newResponse(data, 0, "success");
|
||||
}
|
||||
|
||||
public static <T> Response<T> newFailedResponse(Integer code, String message) {
|
||||
return newResponse(null, code, message);
|
||||
}
|
||||
|
||||
public static <T> Response<T> newFailedResponse(ErrorCode ErrorCode) {
|
||||
return newResponse(null, ErrorCode.getCode(), ErrorCode.getDesc());
|
||||
}
|
||||
|
||||
public static <T> Response<T> newFailedResponse(ErrorCode ErrorCode, String message) {
|
||||
return newResponse(null, ErrorCode.getCode(), message);
|
||||
}
|
||||
|
||||
public static <T> Response<T> newResponse(T data, Integer code, String message) {
|
||||
Response<T> response = new Response<T>();
|
||||
response.setCode(code);
|
||||
response.setMsg(message);
|
||||
if (data != null && data instanceof String && "".equals(data)) {
|
||||
response.setData(null);
|
||||
} else {
|
||||
response.setData(data);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,420 @@
|
|||
package com.guwan.backend.face.rtsp;/*
|
||||
package com.guwan.face.rtsp;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.arcsoft.face.FaceInfo;
|
||||
import com.arcsoft.face.enums.ExtractType;
|
||||
import com.arcsoft.face.toolkit.ImageFactory;
|
||||
import com.arcsoft.face.toolkit.ImageInfo;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.guwan.face.dto.FaceVideoVo;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.PutObjectOptions;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.shapelight.common.config.GlobalValue;
|
||||
import net.shapelight.common.config.MinioConfig;
|
||||
import net.shapelight.common.utils.Constant;
|
||||
import net.shapelight.common.utils.UUIDUtil;
|
||||
import net.shapelight.modules.face.entity.UserCompareInfo;
|
||||
import net.shapelight.modules.face.service.FaceEngineService;
|
||||
import net.shapelight.modules.face.util.UserInfo;
|
||||
import net.shapelight.modules.face.util.UserRamGroup;
|
||||
import net.shapelight.modules.feignClient.CxFeignClient;
|
||||
|
||||
import net.shapelight.modules.ten.dao.TenCellDao;
|
||||
import net.shapelight.modules.ten.entity.TenCellEntity;
|
||||
import net.shapelight.modules.ten.entity.TenPersonEntity;
|
||||
import net.shapelight.modules.ten.service.TenPersonService;
|
||||
import net.shapelight.modules.ten.service.impl.TenPersonServiceImpl;
|
||||
import org.bytedeco.javacpp.avutil;
|
||||
import org.bytedeco.javacv.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.bytedeco.javacpp.avutil.AV_LOG_ERROR;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class RtspFrameGrabber {
|
||||
|
||||
*/
|
||||
/* @Autowired
|
||||
private FaceEngineService faceEngineService;
|
||||
|
||||
@Autowired
|
||||
private TenPersonService personService;
|
||||
|
||||
@Autowired
|
||||
private GlobalValue globalValue;
|
||||
|
||||
@Autowired
|
||||
private CxFeignClient feignClient;
|
||||
@Autowired
|
||||
private TenPersonServiceImpl tenPersonService;
|
||||
|
||||
@Autowired
|
||||
private TenCellDao tenCellDao;
|
||||
|
||||
@Autowired
|
||||
private MinioConfig minioConfig;
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
|
||||
|
||||
*//*
|
||||
|
||||
*/
|
||||
/**
|
||||
* 用于记录人脸识别相关状态
|
||||
*//*
|
||||
|
||||
public ConcurrentHashMap<Integer, Integer> requestFeatureStatusMap = new ConcurrentHashMap<>();
|
||||
*/
|
||||
/**
|
||||
* rtsp视频流url
|
||||
*//*
|
||||
|
||||
@Value("${global.Url.rtspUrl}")
|
||||
private String rtspUrl;
|
||||
*/
|
||||
/**
|
||||
* 帧抓取器
|
||||
*//*
|
||||
|
||||
private FFmpegFrameGrabber grabber;
|
||||
*/
|
||||
/**
|
||||
* 视频帧率
|
||||
*//*
|
||||
|
||||
private int frameRate = 25;
|
||||
*/
|
||||
/**
|
||||
* 视频码率
|
||||
*//*
|
||||
|
||||
// private int bitRate = 2000000;
|
||||
private int bitRate = 128000;
|
||||
*/
|
||||
/**
|
||||
* 视频宽度
|
||||
*//*
|
||||
|
||||
private int frameWidth = 480;
|
||||
*/
|
||||
/**
|
||||
* 视频高度
|
||||
*//*
|
||||
|
||||
private int frameHeight = 270;
|
||||
|
||||
|
||||
|
||||
private void createGrabber() {
|
||||
try {
|
||||
|
||||
grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
|
||||
grabber.setOption("rtsp_transport", "tcp");
|
||||
|
||||
// grabber.setOption("reconnect", "1");
|
||||
// grabber.setOption("reconnect_at_eof", "1");
|
||||
// grabber.setOption("reconnect_streamed", "1");
|
||||
// grabber.setOption("reconnect_delay_max", "2");
|
||||
// grabber.setOption("preset", "veryfast");
|
||||
// grabber.setOption("probesize", "192");
|
||||
// grabber.setOption("tune", "zerolatency");
|
||||
// grabber.setFrameRate(30.0);
|
||||
// grabber.setOption("buffer_size", "" + this.bufferSize);
|
||||
// grabber.setOption("max_delay", "500000");
|
||||
// grabber.setOption("stimeout", String.valueOf(20000));
|
||||
// grabber.setOption("loglevel", "quiet");
|
||||
|
||||
|
||||
// grabber.setOption("appkey", "****");//海康视频 appkey
|
||||
// grabber.setOption("secret", byte2Base64);//海康视频 secret
|
||||
// grabber.setOption("port", "446");//默认443
|
||||
// grabber.setOption("enableHTTPS", "1"); //是否启用HTTPS协议,这里总是填1
|
||||
// grabber.setOption("rtsp_flags", "prefer_tcp");
|
||||
grabber.setOption("stimeout", "5000000");//5秒
|
||||
|
||||
|
||||
//设置帧率
|
||||
grabber.setFrameRate(frameRate);
|
||||
//设置获取的视频宽度
|
||||
grabber.setImageWidth(frameWidth);
|
||||
//设置获取的视频高度
|
||||
grabber.setImageHeight(frameHeight);
|
||||
//设置视频bit率
|
||||
grabber.setVideoBitrate(bitRate);
|
||||
grabber.start();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
public List<FaceVideoVo> startGrabber(String cellId) {
|
||||
|
||||
List<FaceVideoVo> faceVideoVos = new ArrayList<>();
|
||||
|
||||
avutil.av_log_set_level(AV_LOG_ERROR);
|
||||
Java2DFrameConverter java2DFrameConverter = new Java2DFrameConverter();
|
||||
if (grabber == null) {
|
||||
//log.info("连接rtsp:" + rstp + ",开始创建grabber");
|
||||
createGrabber();
|
||||
}
|
||||
try {
|
||||
Frame frame = grabber.grabImage();
|
||||
if (frame != null) {
|
||||
// logger.info("处理帧.............................................");
|
||||
BufferedImage bi = java2DFrameConverter.getBufferedImage(frame);
|
||||
|
||||
byte[] bytes = imageToBytes(bi, "jpg");
|
||||
|
||||
*/
|
||||
/* InputStream frameInputStream = new ByteArrayInputStream(bytes);
|
||||
String frameFileName = "temp/" + "frame_" + UUIDUtil.uuid()
|
||||
+ ".jpg";
|
||||
|
||||
PutObjectOptions framePutObjectOptions = new PutObjectOptions(bytes.length, -1);
|
||||
framePutObjectOptions.setContentType("image/jpeg");
|
||||
|
||||
minioClient.putObject(
|
||||
minioConfig.getBucketName(), frameFileName, frameInputStream, framePutObjectOptions);
|
||||
|
||||
System.out.println("文件名 = " + globalValue.getMinioEndpoint() + "/" +
|
||||
globalValue.getMinioBucketName() + "/" + frameFileName);*//*
|
||||
|
||||
*/
|
||||
/* // 读取图片
|
||||
BufferedImage image = ImageIO.read(new File("C:\\Users\\zhangbo\\OneDrive\\图片\\Camera Roll\\1寸相片.jpg"));
|
||||
|
||||
// 创建字节输出流
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
// 使用ImageIO将BufferedImage编码为byte[]
|
||||
ImageIO.write(image, "jpg", baos);
|
||||
|
||||
// 转换为byte数组
|
||||
byte[] imageBytes = baos.toByteArray();*//*
|
||||
|
||||
faceVideoVos = imageRecognition(bytes, cellId);
|
||||
System.out.println("faceVideoVos = " + faceVideoVos);
|
||||
|
||||
} else {
|
||||
log.error("解码失败");
|
||||
if (grabber != null) {
|
||||
try {
|
||||
grabber.stop();
|
||||
} catch (FrameGrabber.Exception ex) {
|
||||
log.error("grabber stop exception: " + ex.getMessage());
|
||||
} finally {
|
||||
grabber = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
|
||||
if (grabber != null) {
|
||||
try {
|
||||
grabber.stop();
|
||||
} catch (FrameGrabber.Exception ex) {
|
||||
log.error("grabber stop exception: " + ex.getMessage());
|
||||
} finally {
|
||||
grabber = null;
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
grabber = null;
|
||||
}
|
||||
// try {
|
||||
// Thread.sleep(100);
|
||||
// } catch (InterruptedException e) {
|
||||
// logger.error(e.getMessage());
|
||||
// }
|
||||
|
||||
return faceVideoVos;
|
||||
}
|
||||
|
||||
public List<FaceVideoVo> recognition(MultipartFile file,String cellId) {
|
||||
try {
|
||||
byte[] bytes = file.getBytes();
|
||||
return imageRecognition(bytes, cellId);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<FaceVideoVo> imageRecognition(byte[] bytes,String cellId) {
|
||||
List<FaceVideoVo> temp = new ArrayList<>();
|
||||
if (bytes != null && bytes.length > 0) {
|
||||
ImageInfo imageInfo = ImageFactory.getRGBData(bytes);
|
||||
List<FaceInfo> faceInfoList = faceEngineService.detectFaces(imageInfo);
|
||||
|
||||
|
||||
*/
|
||||
/* for (FaceInfo faceInfo : faceInfoList) {
|
||||
System.out.println(faceInfo.getFace3DAngle());
|
||||
//Yaw上下 Pitch左右
|
||||
if (faceInfo.getFace3DAngle().getYaw() > 20 || faceInfo.getFace3DAngle().getYaw() < -20) {
|
||||
System.out.println("没在好好看!!!");
|
||||
// faceInfoList.remove(faceInfo);
|
||||
}
|
||||
if (faceInfo.getFace3DAngle().getPitch() > 20 || faceInfo.getFace3DAngle().getPitch() < -20) {
|
||||
System.out.println("没在好好看!!!");
|
||||
}
|
||||
}*//*
|
||||
|
||||
|
||||
|
||||
if (CollectionUtil.isNotEmpty(faceInfoList)) {
|
||||
faceInfoList.forEach(faceInfo -> {
|
||||
*/
|
||||
/* FaceRecognitionResDTO faceRecognitionResDTO = new FaceRecognitionResDTO();
|
||||
faceRecognitionResDTO.setRect(faceInfo.getRect());*//*
|
||||
|
||||
|
||||
byte[] featureBytes = faceEngineService.extractFaceFeature(imageInfo,
|
||||
faceInfo, ExtractType.REGISTER);
|
||||
if (featureBytes != null) {
|
||||
//底库
|
||||
List<UserInfo> userInfoList = UserRamGroup.getUserList(cellId);
|
||||
//人脸对比 这里长度永远为1
|
||||
List<UserCompareInfo> userCompareInfoList = faceEngineService
|
||||
.faceRecognition(featureBytes, userInfoList, Float.parseFloat(globalValue.getRecFaceThd()));
|
||||
|
||||
for (UserCompareInfo userCompareInfo : userCompareInfoList) {
|
||||
FaceVideoVo faceVideoVo = new FaceVideoVo();
|
||||
if (faceInfo.getFace3DAngle().getYaw() > 20 || faceInfo.getFace3DAngle().getYaw() < -20) {
|
||||
faceVideoVo.setIsHeadOnView(0);
|
||||
}else if (faceInfo.getFace3DAngle().getPitch() > 20 || faceInfo.getFace3DAngle().getPitch() < -20) {
|
||||
faceVideoVo.setIsHeadOnView(0);
|
||||
}else {
|
||||
faceVideoVo.setIsHeadOnView(1);
|
||||
}
|
||||
|
||||
faceVideoVo.setPersonId(userCompareInfo.getFaceId()).setName(userCompareInfo.getName());
|
||||
|
||||
|
||||
|
||||
InputStream frameInputStream = new ByteArrayInputStream(bytes);
|
||||
String frameFileName = "temp/" + "frame_" + UUIDUtil.uuid()
|
||||
+ ".jpg";
|
||||
|
||||
PutObjectOptions framePutObjectOptions = new PutObjectOptions(bytes.length, -1);
|
||||
framePutObjectOptions.setContentType("image/jpeg");
|
||||
|
||||
try {
|
||||
frameInputStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
try {
|
||||
minioClient.putObject(
|
||||
minioConfig.getBucketName(), frameFileName, frameInputStream, framePutObjectOptions);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
System.out.println("文件名 = " + globalValue.getMinioEndpoint() + "/" +
|
||||
globalValue.getMinioBucketName() + "/" + frameFileName);
|
||||
|
||||
|
||||
|
||||
faceVideoVo.setImageUrl(frameFileName);
|
||||
|
||||
temp.add(faceVideoVo);
|
||||
}
|
||||
if (!userCompareInfoList.isEmpty()) {
|
||||
|
||||
for (UserCompareInfo userCompareInfo : userCompareInfoList) {
|
||||
TenPersonEntity tenPerson = personService.getOne(new LambdaQueryWrapper<TenPersonEntity>()
|
||||
.eq(TenPersonEntity::getPersonId, userCompareInfo.getFaceId()));
|
||||
Map<Integer, String> personnelTypeMap = new HashMap<>();
|
||||
personnelTypeMap.put(Constant.PERSON_TYPE_OWNER, "2");//内部人员
|
||||
personnelTypeMap.put(Constant.PERSON_TYPE_MEMBER, "1");//承包商
|
||||
personnelTypeMap.put(Constant.PERSON_TYPE_TENANT, "3");//长期供应商
|
||||
personnelTypeMap.put(Constant.PERSON_TYPE_GUEST, "4");//访客
|
||||
|
||||
//
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("pmWatchVideoRecordId", "");
|
||||
params.put("orgId", tenPerson.getOrgId());
|
||||
params.put("orgName", tenCellDao.selectOne(new QueryWrapper<TenCellEntity>().eq("cell_id", tenPerson.getCellId())));
|
||||
params.put("personnelName", tenPerson.getName());
|
||||
params.put("personnelId", tenPerson.getOpenId());
|
||||
params.put("personnelCardId", tenPerson.getIdCard());
|
||||
params.put("personnelType", personnelTypeMap.get(tenPerson.getPersonType()));
|
||||
params.put("dictSex", tenPerson.getGender() == 0 ? "女" : "男");
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
|
||||
params.put("watchVideoTime", sdf.format(System.currentTimeMillis()));
|
||||
tenPerson.setIsWatchSafeVideo(1);
|
||||
tenPersonService.updateById(tenPerson);
|
||||
|
||||
System.out.println("params = " + params);
|
||||
|
||||
*/
|
||||
/* JSONObject jsonObject = feignClient.savePmWatchVideoRecord(params);
|
||||
if (jsonObject.getBool("success") != null && jsonObject.getBool("success")) {
|
||||
personService.update(new LambdaUpdateWrapper<TenPersonEntity>()
|
||||
.set(TenPersonEntity::getIsWatchSafeVideo, 1)
|
||||
.eq(TenPersonEntity::getPersonId, userCompareInfo.getFaceId()));
|
||||
}*//*
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
log.error("图片不合格,未检测到人脸");
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
log.error("图片不合格,未检测到人脸");
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
*/
|
||||
/**
|
||||
* 图片转字节数组
|
||||
*
|
||||
* @param _bi 图片数据
|
||||
* @return 图片字节码
|
||||
*//*
|
||||
|
||||
private byte[] imageToBytes(BufferedImage _bi, String _format) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(_bi, _format, baos);
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,28 @@
|
|||
package com.guwan.backend.face.service;
|
||||
|
||||
|
||||
import com.arcsoft.face.FaceInfo;
|
||||
import com.arcsoft.face.enums.ExtractType;
|
||||
import com.arcsoft.face.toolkit.ImageInfo;
|
||||
import com.guwan.face.entity.ProcessInfo;
|
||||
import com.guwan.face.entity.UserCompareInfo;
|
||||
import com.guwan.face.util.UserInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface FaceEngineService {
|
||||
|
||||
List<FaceInfo> detectFaces(ImageInfo imageInfo);
|
||||
|
||||
Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) ;
|
||||
|
||||
byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType);
|
||||
|
||||
List<UserCompareInfo> faceRecognition(byte[] faceFeature, List<UserInfo> userInfoList, float passRate) ;
|
||||
|
||||
List<ProcessInfo> process(ImageInfo imageInfo, List<FaceInfo> faceInfoList);
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
package com.guwan.backend.face.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.arcsoft.face.*;
|
||||
import com.arcsoft.face.enums.DetectMode;
|
||||
import com.arcsoft.face.enums.DetectOrient;
|
||||
import com.arcsoft.face.enums.ExtractType;
|
||||
import com.arcsoft.face.toolkit.ImageInfo;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.guwan.face.entity.ProcessInfo;
|
||||
import com.guwan.face.entity.UserCompareInfo;
|
||||
import com.guwan.face.enums.ErrorCodeEnum;
|
||||
import com.guwan.face.factory.FaceEngineFactory;
|
||||
import com.guwan.face.rpc.BusinessException;
|
||||
import com.guwan.face.service.FaceEngineService;
|
||||
import com.guwan.face.util.UserInfo;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
|
||||
@Service("faceEngineService")
|
||||
@Slf4j
|
||||
public class FaceEngineServiceImpl implements FaceEngineService {
|
||||
|
||||
public final static Logger logger = LoggerFactory.getLogger(FaceEngineServiceImpl.class);
|
||||
|
||||
@Value("${config.arcface-sdk.app-id}")
|
||||
public String appId;
|
||||
|
||||
@Value("${config.arcface-sdk.sdk-key}")
|
||||
public String sdkKey;
|
||||
|
||||
@Value("${config.arcface-sdk.active-key}")
|
||||
public String activeKey;
|
||||
|
||||
@Value("${config.arcface-sdk.active-file}")
|
||||
public String activeFile;
|
||||
|
||||
@Value("${config.arcface-sdk.detect-pool-size}")
|
||||
public Integer detectPooSize;
|
||||
|
||||
@Value("${config.arcface-sdk.compare-pool-size}")
|
||||
public Integer comparePooSize;
|
||||
|
||||
private ExecutorService compareExecutorService;
|
||||
|
||||
//通用人脸识别引擎池
|
||||
private GenericObjectPool<FaceEngine> faceEngineGeneralPool;
|
||||
|
||||
//人脸比对引擎池
|
||||
private GenericObjectPool<FaceEngine> faceEngineComparePool;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
||||
|
||||
GenericObjectPoolConfig detectPoolConfig = new GenericObjectPoolConfig();
|
||||
detectPoolConfig.setMaxIdle(detectPooSize);
|
||||
detectPoolConfig.setMaxTotal(detectPooSize);
|
||||
detectPoolConfig.setMinIdle(detectPooSize);
|
||||
detectPoolConfig.setLifo(false);
|
||||
EngineConfiguration detectCfg = new EngineConfiguration();
|
||||
FunctionConfiguration detectFunctionCfg = new FunctionConfiguration();
|
||||
detectFunctionCfg.setSupportFaceDetect(true);//开启人脸检测功能
|
||||
detectFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能
|
||||
detectFunctionCfg.setSupportAge(true);//开启年龄检测功能
|
||||
detectFunctionCfg.setSupportGender(true);//开启性别检测功能
|
||||
detectFunctionCfg.setSupportLiveness(true);//开启活体检测功能
|
||||
detectCfg.setFunctionConfiguration(detectFunctionCfg);
|
||||
detectCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式,如果是连续帧的视频流图片,那么改成VIDEO模式
|
||||
detectCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);//人脸旋转角度
|
||||
faceEngineGeneralPool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey, activeFile, detectCfg), detectPoolConfig);//底层库算法对象池
|
||||
|
||||
|
||||
//初始化特征比较线程池
|
||||
GenericObjectPoolConfig comparePoolConfig = new GenericObjectPoolConfig();
|
||||
comparePoolConfig.setMaxIdle(comparePooSize);
|
||||
comparePoolConfig.setMaxTotal(comparePooSize);
|
||||
comparePoolConfig.setMinIdle(comparePooSize);
|
||||
comparePoolConfig.setLifo(false);
|
||||
EngineConfiguration compareCfg = new EngineConfiguration();
|
||||
FunctionConfiguration compareFunctionCfg = new FunctionConfiguration();
|
||||
compareFunctionCfg.setSupportFaceRecognition(true);//开启人脸识别功能
|
||||
compareCfg.setFunctionConfiguration(compareFunctionCfg);
|
||||
compareCfg.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);//图片检测模式,如果是连续帧的视频流图片,那么改成VIDEO模式
|
||||
compareCfg.setDetectFaceOrientPriority(DetectOrient.ASF_OP_0_ONLY);//人脸旋转角度
|
||||
faceEngineComparePool = new GenericObjectPool(new FaceEngineFactory(appId, sdkKey, activeKey, activeFile, compareCfg), comparePoolConfig);//底层库算法对象池
|
||||
compareExecutorService = Executors.newFixedThreadPool(comparePooSize);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<FaceInfo> detectFaces(ImageInfo imageInfo) {
|
||||
|
||||
FaceEngine faceEngine = null;
|
||||
try {
|
||||
faceEngine = faceEngineGeneralPool.borrowObject();
|
||||
if (faceEngine == null) {
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
|
||||
}
|
||||
|
||||
//人脸检测得到人脸列表
|
||||
List<FaceInfo> faceInfoList = new ArrayList<>();
|
||||
//人脸检测
|
||||
int errorCode = faceEngine.detectFaces(imageInfo.getImageData(),
|
||||
imageInfo.getWidth(), imageInfo.getHeight(),
|
||||
imageInfo.getImageFormat(), faceInfoList);
|
||||
if (errorCode == 0) {
|
||||
return faceInfoList;
|
||||
} else {
|
||||
log.error("人脸检测失败,errorCode:" + errorCode);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
} finally {
|
||||
if (faceEngine != null) {
|
||||
//释放引擎对象
|
||||
faceEngineGeneralPool.returnObject(faceEngine);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) {
|
||||
|
||||
List<FaceInfo> faceInfoList1 = detectFaces(imageInfo1);
|
||||
List<FaceInfo> faceInfoList2 = detectFaces(imageInfo2);
|
||||
|
||||
if (CollectionUtil.isEmpty(faceInfoList1)) {
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "照片1未检测到人脸");
|
||||
}
|
||||
if (CollectionUtil.isEmpty(faceInfoList2)) {
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "照片2未检测到人脸");
|
||||
}
|
||||
|
||||
byte[] feature1 = extractFaceFeature(imageInfo1, faceInfoList1.get(0), ExtractType.REGISTER);
|
||||
byte[] feature2 = extractFaceFeature(imageInfo2, faceInfoList2.get(0), ExtractType.RECOGNIZE);
|
||||
|
||||
FaceEngine faceEngine = null;
|
||||
try {
|
||||
faceEngine = faceEngineGeneralPool.borrowObject();
|
||||
if (faceEngine == null) {
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
|
||||
}
|
||||
|
||||
FaceFeature faceFeature1 = new FaceFeature();
|
||||
faceFeature1.setFeatureData(feature1);
|
||||
FaceFeature faceFeature2 = new FaceFeature();
|
||||
faceFeature2.setFeatureData(feature2);
|
||||
//提取人脸特征
|
||||
FaceSimilar faceSimilar = new FaceSimilar();
|
||||
int errorCode = faceEngine.compareFaceFeature(faceFeature1, faceFeature2, faceSimilar);
|
||||
if (errorCode == 0) {
|
||||
return faceSimilar.getScore();
|
||||
} else {
|
||||
log.error("特征提取失败,errorCode:" + errorCode);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
} finally {
|
||||
if (faceEngine != null) {
|
||||
//释放引擎对象
|
||||
faceEngineGeneralPool.returnObject(faceEngine);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸特征
|
||||
*
|
||||
* @param imageInfo
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType) {
|
||||
|
||||
FaceEngine faceEngine = null;
|
||||
try {
|
||||
faceEngine = faceEngineGeneralPool.borrowObject();
|
||||
if (faceEngine == null) {
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
|
||||
}
|
||||
|
||||
FaceFeature faceFeature = new FaceFeature();
|
||||
//提取人脸特征
|
||||
int errorCode = faceEngine.extractFaceFeature(imageInfo, faceInfo, extractType, 0, faceFeature);
|
||||
if (errorCode == 0) {
|
||||
return faceFeature.getFeatureData();
|
||||
} else {
|
||||
log.error("特征提取失败,errorCode:" + errorCode);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("", e);
|
||||
} finally {
|
||||
if (faceEngine != null) {
|
||||
//释放引擎对象
|
||||
faceEngineGeneralPool.returnObject(faceEngine);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCompareInfo> faceRecognition(byte[] faceFeature, List<UserInfo> userInfoList, float passRate) {
|
||||
List<UserCompareInfo> resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表
|
||||
|
||||
FaceFeature targetFaceFeature = new FaceFeature();
|
||||
targetFaceFeature.setFeatureData(faceFeature);
|
||||
|
||||
List<List<UserInfo>> faceUserInfoPartList = Lists.partition(userInfoList, 1000);//分成1000一组,多线程处理
|
||||
CompletionService<List<UserCompareInfo>> completionService = new ExecutorCompletionService(compareExecutorService);
|
||||
for (List<UserInfo> part : faceUserInfoPartList) {
|
||||
completionService.submit(new CompareFaceTask(part, targetFaceFeature, passRate));
|
||||
}
|
||||
for (int i = 0; i < faceUserInfoPartList.size(); i++) {
|
||||
List<UserCompareInfo> faceUserInfoList = null;
|
||||
try {
|
||||
faceUserInfoList = completionService.take().get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
}
|
||||
if (CollectionUtil.isNotEmpty(userInfoList)) {
|
||||
resultUserInfoList.addAll(faceUserInfoList);
|
||||
}
|
||||
}
|
||||
|
||||
resultUserInfoList.sort((h1, h2) -> h2.getSimilar().compareTo(h1.getSimilar()));//从大到小排序
|
||||
|
||||
return resultUserInfoList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProcessInfo> process(ImageInfo imageInfo, List<FaceInfo> faceInfoList) {
|
||||
FaceEngine faceEngine = null;
|
||||
try {
|
||||
//获取引擎对象
|
||||
faceEngine = faceEngineGeneralPool.borrowObject();
|
||||
if (faceEngine == null) {
|
||||
throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败");
|
||||
}
|
||||
|
||||
|
||||
int errorCode = faceEngine.process(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), imageInfo.getImageFormat(), faceInfoList, FunctionConfiguration.builder().supportAge(true).supportGender(true).supportLiveness(true).build());
|
||||
if (errorCode == 0) {
|
||||
List<ProcessInfo> processInfoList = Lists.newLinkedList();
|
||||
|
||||
//性别列表
|
||||
List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
|
||||
faceEngine.getGender(genderInfoList);
|
||||
|
||||
//年龄列表
|
||||
List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
|
||||
faceEngine.getAge(ageInfoList);
|
||||
//活体结果列表
|
||||
List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
|
||||
faceEngine.getLiveness(livenessInfoList);
|
||||
|
||||
|
||||
for (int i = 0; i < genderInfoList.size(); i++) {
|
||||
ProcessInfo processInfo = new ProcessInfo();
|
||||
processInfo.setGender(genderInfoList.get(i).getGender());
|
||||
processInfo.setAge(ageInfoList.get(i).getAge());
|
||||
processInfo.setLiveness(livenessInfoList.get(i).getLiveness());
|
||||
processInfoList.add(processInfo);
|
||||
}
|
||||
return processInfoList;
|
||||
|
||||
}
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("", e);
|
||||
} finally {
|
||||
if (faceEngine != null) {
|
||||
//释放引擎对象
|
||||
faceEngineGeneralPool.returnObject(faceEngine);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class CompareFaceTask implements Callable<List<UserCompareInfo>> {
|
||||
|
||||
private List<UserInfo> userInfoList;
|
||||
private FaceFeature targetFaceFeature;
|
||||
private float passRate;
|
||||
|
||||
|
||||
public CompareFaceTask(List<UserInfo> userInfoList, FaceFeature targetFaceFeature, float passRate) {
|
||||
this.userInfoList = userInfoList;
|
||||
this.targetFaceFeature = targetFaceFeature;
|
||||
this.passRate = passRate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UserCompareInfo> call() throws Exception {
|
||||
FaceEngine faceEngine = null;
|
||||
List<UserCompareInfo> resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表
|
||||
try {
|
||||
faceEngine = faceEngineComparePool.borrowObject();
|
||||
for (UserInfo userInfo : userInfoList) {
|
||||
FaceFeature sourceFaceFeature = new FaceFeature();
|
||||
sourceFaceFeature.setFeatureData(userInfo.getFaceFeature());
|
||||
FaceSimilar faceSimilar = new FaceSimilar();
|
||||
faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
|
||||
if (faceSimilar.getScore() > passRate) {//相似值大于配置预期,加入到识别到人脸的列表
|
||||
UserCompareInfo info = new UserCompareInfo();
|
||||
info.setName(userInfo.getName());
|
||||
info.setFaceId(userInfo.getFaceId());
|
||||
info.setSimilar(faceSimilar.getScore());
|
||||
resultUserInfoList.add(info);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("", e);
|
||||
} finally {
|
||||
if (faceEngine != null) {
|
||||
faceEngineComparePool.returnObject(faceEngine);
|
||||
}
|
||||
}
|
||||
|
||||
return resultUserInfoList;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.guwan.backend.face.util;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class Base64Util {
|
||||
public static String base64Process(String base64Str) {
|
||||
if (!ObjectUtils.isEmpty(base64Str)) {
|
||||
String photoBase64 = base64Str.substring(0, 30).toLowerCase();
|
||||
int indexOf = photoBase64.indexOf("base64,");
|
||||
if (indexOf > 0) {
|
||||
base64Str = base64Str.substring(indexOf + 7);
|
||||
}
|
||||
base64Str = base64Str.replaceAll(" ", "+");
|
||||
base64Str = base64Str.replaceAll("\r|\n", "");
|
||||
return base64Str;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static byte[] base64ToBytes(String base64) {
|
||||
if (ObjectUtils.isEmpty(base64)) {
|
||||
return null;
|
||||
}
|
||||
String base64Process = base64Process(base64);
|
||||
|
||||
byte[] decode = Base64.getDecoder().decode(base64Process);
|
||||
return decode;
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
package com.guwan.backend.face.util;
|
||||
|
||||
import com.arcsoft.face.*;
|
||||
import com.arcsoft.face.enums.*;
|
||||
import com.arcsoft.face.toolkit.ImageFactory;
|
||||
import com.arcsoft.face.toolkit.ImageInfo;
|
||||
import com.arcsoft.face.toolkit.ImageInfoEx;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FaceEngineTest {
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
//激活码,从官网获取
|
||||
String appId = "DEnAZa1bWXcaAxyWUg33QZaKCmMkNmrQxuKZJQGmZsHJ";
|
||||
String sdkKey = "vWbvUyStZeartSaM6QoTzPYWFpSaj4uhfDmRifSzCd6";
|
||||
String activeKey = "82G1-11QA-713Y-8NB4";
|
||||
|
||||
System.err.println("注意,如果返回的errorCode不为0,可查看com.arcsoft.face.enums.ErrorInfo类获取相应的错误信息");
|
||||
|
||||
//人脸识别引擎库存放路径
|
||||
FaceEngine faceEngine = new FaceEngine("/home/huangyifang/gb/咸阳师范/ArcSoft_ArcFacePro_linux_java_V4.1/libs/LINUX64");
|
||||
//激活引擎
|
||||
int errorCode = faceEngine.activeOnline(appId, sdkKey, activeKey);
|
||||
System.out.println("引擎激活errorCode:" + errorCode);
|
||||
|
||||
ActiveDeviceInfo activeDeviceInfo = new ActiveDeviceInfo();
|
||||
//采集设备信息(可离线)
|
||||
errorCode = faceEngine.getActiveDeviceInfo(activeDeviceInfo);
|
||||
System.out.println("采集设备信息errorCode:" + errorCode);
|
||||
System.out.println("设备信息:" + activeDeviceInfo.getDeviceInfo());
|
||||
|
||||
// faceEngine.activeOffline("d:\\ArcFacePro64.dat.offline");
|
||||
|
||||
ActiveFileInfo activeFileInfo = new ActiveFileInfo();
|
||||
errorCode = faceEngine.getActiveFileInfo(activeFileInfo);
|
||||
System.out.println("获取激活文件errorCode:" + errorCode);
|
||||
System.out.println("激活文件信息:" + activeFileInfo.toString());
|
||||
|
||||
//引擎配置
|
||||
EngineConfiguration engineConfiguration = new EngineConfiguration();
|
||||
engineConfiguration.setDetectMode(DetectMode.ASF_DETECT_MODE_IMAGE);
|
||||
engineConfiguration.setDetectFaceOrientPriority(DetectOrient.ASF_OP_ALL_OUT);
|
||||
engineConfiguration.setDetectFaceMaxNum(10);
|
||||
//功能配置
|
||||
FunctionConfiguration functionConfiguration = new FunctionConfiguration();
|
||||
functionConfiguration.setSupportAge(true);
|
||||
functionConfiguration.setSupportFaceDetect(true);
|
||||
functionConfiguration.setSupportFaceRecognition(true);
|
||||
functionConfiguration.setSupportGender(true);
|
||||
functionConfiguration.setSupportLiveness(true);
|
||||
functionConfiguration.setSupportIRLiveness(true);
|
||||
functionConfiguration.setSupportImageQuality(true);
|
||||
functionConfiguration.setSupportMaskDetect(true);
|
||||
functionConfiguration.setSupportUpdateFaceData(true);
|
||||
engineConfiguration.setFunctionConfiguration(functionConfiguration);
|
||||
|
||||
//初始化引擎
|
||||
errorCode = faceEngine.init(engineConfiguration);
|
||||
System.out.println("初始化引擎errorCode:" + errorCode);
|
||||
VersionInfo version = faceEngine.getVersion();
|
||||
System.out.println(version);
|
||||
|
||||
//人脸检测
|
||||
ImageInfo imageInfo = ImageFactory.getRGBData(new File("/home/huangyifang/gb/咸阳师范/10.jpg"));
|
||||
List<FaceInfo> faceInfoList = new ArrayList<FaceInfo>();
|
||||
errorCode = faceEngine.detectFaces(imageInfo, faceInfoList);
|
||||
System.out.println("人脸检测errorCode:" + errorCode);
|
||||
System.out.println("检测到人脸数:" + faceInfoList.size());
|
||||
|
||||
ImageQuality imageQuality = new ImageQuality();
|
||||
errorCode = faceEngine.imageQualityDetect(imageInfo, faceInfoList.get(0), 0, imageQuality);
|
||||
System.out.println("图像质量检测errorCode:" + errorCode);
|
||||
System.out.println("图像质量分数:" + imageQuality.getFaceQuality());
|
||||
|
||||
//特征提取
|
||||
FaceFeature faceFeature = new FaceFeature();
|
||||
errorCode = faceEngine.extractFaceFeature(imageInfo, faceInfoList.get(0), ExtractType.REGISTER, 0, faceFeature);
|
||||
System.out.println("特征提取errorCode:" + errorCode);
|
||||
|
||||
//人脸检测2
|
||||
ImageInfo imageInfo2 = ImageFactory.getRGBData(new File("/home/huangyifang/gb/咸阳师范/10.jpg"));
|
||||
List<FaceInfo> faceInfoList2 = new ArrayList<FaceInfo>();
|
||||
errorCode = faceEngine.detectFaces(imageInfo2, faceInfoList2);
|
||||
System.out.println("人脸检测errorCode:" + errorCode);
|
||||
System.out.println("检测到人脸数:" + faceInfoList.size());
|
||||
|
||||
//特征提取2
|
||||
FaceFeature faceFeature2 = new FaceFeature();
|
||||
errorCode = faceEngine.extractFaceFeature(imageInfo2, faceInfoList2.get(0), ExtractType.RECOGNIZE, 0, faceFeature2);
|
||||
System.out.println("特征提取errorCode:" + errorCode);
|
||||
|
||||
//特征比对
|
||||
FaceFeature targetFaceFeature = new FaceFeature();
|
||||
targetFaceFeature.setFeatureData(faceFeature.getFeatureData());
|
||||
FaceFeature sourceFaceFeature = new FaceFeature();
|
||||
sourceFaceFeature.setFeatureData(faceFeature2.getFeatureData());
|
||||
FaceSimilar faceSimilar = new FaceSimilar();
|
||||
|
||||
errorCode = faceEngine.compareFaceFeature(targetFaceFeature, sourceFaceFeature, faceSimilar);
|
||||
System.out.println("特征比对errorCode:" + errorCode);
|
||||
System.out.println("人脸相似度:" + faceSimilar.getScore());
|
||||
|
||||
|
||||
//人脸属性检测
|
||||
FunctionConfiguration configuration = new FunctionConfiguration();
|
||||
configuration.setSupportAge(true);
|
||||
configuration.setSupportGender(true);
|
||||
configuration.setSupportLiveness(true);
|
||||
configuration.setSupportMaskDetect(true);
|
||||
errorCode = faceEngine.process(imageInfo, faceInfoList, configuration);
|
||||
System.out.println("图像属性处理errorCode:" + errorCode);
|
||||
|
||||
//性别检测
|
||||
List<GenderInfo> genderInfoList = new ArrayList<GenderInfo>();
|
||||
errorCode = faceEngine.getGender(genderInfoList);
|
||||
System.out.println("性别:" + genderInfoList.get(0).getGender());
|
||||
|
||||
//年龄检测
|
||||
List<AgeInfo> ageInfoList = new ArrayList<AgeInfo>();
|
||||
errorCode = faceEngine.getAge(ageInfoList);
|
||||
System.out.println("年龄:" + ageInfoList.get(0).getAge());
|
||||
|
||||
//活体检测
|
||||
List<LivenessInfo> livenessInfoList = new ArrayList<LivenessInfo>();
|
||||
errorCode = faceEngine.getLiveness(livenessInfoList);
|
||||
System.out.println("活体:" + livenessInfoList.get(0).getLiveness());
|
||||
|
||||
//口罩检测
|
||||
List<MaskInfo> maskInfoList = new ArrayList<MaskInfo>();
|
||||
errorCode = faceEngine.getMask(maskInfoList);
|
||||
System.out.println("口罩:" + maskInfoList.get(0).getMask());
|
||||
|
||||
|
||||
//IR属性处理
|
||||
ImageInfo imageInfoGray = ImageFactory.getGrayData(new File("/home/huangyifang/gb/咸阳师范/10.jpg"));
|
||||
List<FaceInfo> faceInfoListGray = new ArrayList<FaceInfo>();
|
||||
errorCode = faceEngine.detectFaces(imageInfoGray, faceInfoListGray);
|
||||
|
||||
FunctionConfiguration configuration2 = new FunctionConfiguration();
|
||||
configuration2.setSupportIRLiveness(true);
|
||||
errorCode = faceEngine.processIr(imageInfoGray, faceInfoListGray, configuration2);
|
||||
//IR活体检测
|
||||
List<IrLivenessInfo> irLivenessInfo = new ArrayList<>();
|
||||
errorCode = faceEngine.getLivenessIr(irLivenessInfo);
|
||||
System.out.println("IR活体:" + irLivenessInfo.get(0).getLiveness());
|
||||
|
||||
//获取激活文件信息
|
||||
ActiveFileInfo activeFileInfo2 = new ActiveFileInfo();
|
||||
errorCode = faceEngine.getActiveFileInfo(activeFileInfo2);
|
||||
|
||||
//更新人脸数据
|
||||
errorCode = faceEngine.updateFaceData(imageInfo, faceInfoList);
|
||||
|
||||
//高级人脸图像处理接口
|
||||
ImageInfoEx imageInfoEx = new ImageInfoEx();
|
||||
imageInfoEx.setHeight(imageInfo.getHeight());
|
||||
imageInfoEx.setWidth(imageInfo.getWidth());
|
||||
imageInfoEx.setImageFormat(imageInfo.getImageFormat());
|
||||
imageInfoEx.setImageDataPlanes(new byte[][]{imageInfo.getImageData()});
|
||||
imageInfoEx.setImageStrides(new int[]{imageInfo.getWidth() * 3});
|
||||
List<FaceInfo> faceInfoList1 = new ArrayList<>();
|
||||
errorCode = faceEngine.detectFaces(imageInfoEx, DetectModel.ASF_DETECT_MODEL_RGB, faceInfoList1);
|
||||
ImageQuality imageQuality1 = new ImageQuality();
|
||||
errorCode = faceEngine.imageQualityDetect(imageInfoEx, faceInfoList1.get(0), 0, imageQuality1);
|
||||
FunctionConfiguration fun = new FunctionConfiguration();
|
||||
fun.setSupportAge(true);
|
||||
errorCode = faceEngine.process(imageInfoEx, faceInfoList1, fun);
|
||||
List<AgeInfo> ageInfoList1 = new ArrayList<>();
|
||||
int age = faceEngine.getAge(ageInfoList1);
|
||||
FaceFeature feature = new FaceFeature();
|
||||
errorCode = faceEngine.extractFaceFeature(imageInfoEx, faceInfoList1.get(0), ExtractType.REGISTER, 0, feature);
|
||||
errorCode = faceEngine.updateFaceData(imageInfoEx, faceInfoList1);
|
||||
|
||||
//设置活体测试
|
||||
errorCode = faceEngine.setLivenessParam(0.5f, 0.7f, 0.3f);
|
||||
System.out.println("设置活体活体阈值errorCode:" + errorCode);
|
||||
|
||||
LivenessParam livenessParam=new LivenessParam();
|
||||
errorCode = faceEngine.getLivenessParam(livenessParam);
|
||||
|
||||
//注册人脸信息1
|
||||
FaceFeatureInfo faceFeatureInfo = new FaceFeatureInfo();
|
||||
faceFeatureInfo.setSearchId(5);
|
||||
faceFeatureInfo.setFaceTag("FeatureData1");
|
||||
faceFeatureInfo.setFeatureData(faceFeature.getFeatureData());
|
||||
errorCode = faceEngine.registerFaceFeature(faceFeatureInfo);
|
||||
|
||||
//注册人脸信息2
|
||||
FaceFeatureInfo faceFeatureInfo2 = new FaceFeatureInfo();
|
||||
faceFeatureInfo2.setSearchId(6);
|
||||
faceFeatureInfo2.setFaceTag("FeatureData2");
|
||||
faceFeatureInfo2.setFeatureData(faceFeature2.getFeatureData());
|
||||
errorCode = faceEngine.registerFaceFeature(faceFeatureInfo2);
|
||||
|
||||
//获取注册人脸个数
|
||||
FaceSearchCount faceSearchCount = new FaceSearchCount();
|
||||
errorCode = faceEngine.getFaceCount(faceSearchCount);
|
||||
System.out.println("注册人脸个数:" + faceSearchCount.getCount());
|
||||
|
||||
//搜索最相似人脸
|
||||
SearchResult searchResult = new SearchResult();
|
||||
errorCode = faceEngine.searchFaceFeature(faceFeature, CompareModel.LIFE_PHOTO, searchResult);
|
||||
System.out.println("最相似人脸Id:" + searchResult.getFaceFeatureInfo().getSearchId());
|
||||
|
||||
//更新人脸信息
|
||||
FaceFeatureInfo faceFeatureInfo3 = new FaceFeatureInfo();
|
||||
faceFeatureInfo3.setSearchId(6);
|
||||
faceFeatureInfo3.setFaceTag("FeatureData2Update");
|
||||
faceFeatureInfo3.setFeatureData(faceFeature2.getFeatureData());
|
||||
errorCode = faceEngine.updateFaceFeature(faceFeatureInfo3);
|
||||
|
||||
//移除人脸信息
|
||||
errorCode = faceEngine.removeFaceFeature(6);
|
||||
|
||||
//引擎卸载
|
||||
errorCode = faceEngine.unInit();
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.guwan.backend.face.util;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class UserInfo {
|
||||
private String faceId;
|
||||
private String name;
|
||||
private byte[] faceFeature;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.guwan.backend.face.util;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class UserRamCache {
|
||||
|
||||
private final ConcurrentHashMap<String, UserInfo> USER_INFO_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private final Set<Listener> REGISTER = new CopyOnWriteArraySet<>();
|
||||
|
||||
public void addUser(UserInfo userInfo) {
|
||||
USER_INFO_MAP.put(userInfo.getFaceId(), userInfo);
|
||||
for (Listener listener : REGISTER) {
|
||||
listener.onAdd(userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeUser(String faceId) {
|
||||
UserInfo userInfo = USER_INFO_MAP.remove(faceId);
|
||||
for (Listener listener : REGISTER) {
|
||||
listener.onRemove(userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public List<UserInfo> getUserList() {
|
||||
List<UserInfo> userInfoList = Lists.newLinkedList();
|
||||
userInfoList.addAll(USER_INFO_MAP.values());
|
||||
return userInfoList;
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
USER_INFO_MAP.clear();
|
||||
REGISTER.clear();
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
REGISTER.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
REGISTER.remove(listener);
|
||||
}
|
||||
|
||||
// @Data
|
||||
// public class UserInfo {
|
||||
//
|
||||
// private String faceId;
|
||||
// private String name;
|
||||
// private byte[] faceFeature;
|
||||
//
|
||||
// }
|
||||
|
||||
public interface Listener {
|
||||
default void onAdd(UserInfo userInfo) {
|
||||
}
|
||||
|
||||
default void onRemove(UserInfo userInfo) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package com.guwan.backend.face.util;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class UserRamGroup {
|
||||
|
||||
private static final ConcurrentHashMap<String, UserRamCache> USER_RAM_GROUP_MAP = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentHashMap<String, String> ORG_CELL_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static void addCell(String cellId){
|
||||
UserRamCache cell = new UserRamCache();
|
||||
USER_RAM_GROUP_MAP.put(cellId,cell);
|
||||
}
|
||||
|
||||
public static void removeCell(String cellId){
|
||||
USER_RAM_GROUP_MAP.remove(cellId);
|
||||
}
|
||||
|
||||
public static void addUser(UserInfo userInfo, String cellId) {
|
||||
USER_RAM_GROUP_MAP.get(cellId).addUser(userInfo);
|
||||
}
|
||||
|
||||
public static void removeUser(String faceId, String cellId) {
|
||||
USER_RAM_GROUP_MAP.get(cellId).removeUser(faceId);
|
||||
}
|
||||
|
||||
public static List<UserInfo> getUserList(String cellId) {
|
||||
return USER_RAM_GROUP_MAP.get(cellId).getUserList();
|
||||
}
|
||||
|
||||
public static void addOrgId(String orgId,String cellId){
|
||||
ORG_CELL_MAP.put(orgId,cellId);
|
||||
}
|
||||
public static String getOrgCellMap(String orgId) {
|
||||
return ORG_CELL_MAP.get(orgId);
|
||||
}
|
||||
|
||||
public static void clear(){
|
||||
USER_RAM_GROUP_MAP.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.guwan.backend.mapper;
|
||||
|
||||
import com.guwan.backend.pojo.dto.BSCategory;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author 12455
|
||||
* @description 针对表【category】的数据库操作Mapper
|
||||
* @createDate 2025-03-13 23:00:51
|
||||
* @Entity com.guwan.backend.pojo.dto.BSCategory
|
||||
*/
|
||||
public interface BSCategoryMapper extends BaseMapper<BSCategory> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.guwan.backend.mapper;
|
||||
|
||||
import com.guwan.backend.pojo.Courses;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
|
||||
/**
|
||||
* @author 12455
|
||||
* @description 针对表【courses(课程表)】的数据库操作Mapper
|
||||
* @createDate 2025-03-13 22:45:19
|
||||
* @Entity com.guwan.backend.pojo.Courses
|
||||
*/
|
||||
public interface CoursesMapper extends BaseMapper<Courses> {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.guwan.backend.pojo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 课程表
|
||||
* @TableName courses
|
||||
*/
|
||||
@TableName(value ="courses")
|
||||
@Data
|
||||
public class Courses implements Serializable {
|
||||
/**
|
||||
* 课程ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 课程标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 分类编码
|
||||
*/
|
||||
private String categoryId;
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 封面图片URL
|
||||
*/
|
||||
private String coverImg;
|
||||
|
||||
/**
|
||||
* 学习人数
|
||||
*/
|
||||
private Integer studentCount;
|
||||
|
||||
/**
|
||||
* 评分
|
||||
*/
|
||||
private BigDecimal rating;
|
||||
|
||||
/**
|
||||
* 价格
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.guwan.backend.pojo.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import java.io.Serializable;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
*
|
||||
* @TableName category
|
||||
*/
|
||||
@TableName(value ="category")
|
||||
@Data
|
||||
public class BSCategory implements Serializable {
|
||||
/**
|
||||
* 种类编号
|
||||
*/
|
||||
@TableId
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 种类名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.guwan.backend.pojo.dto;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 课程表
|
||||
* @TableName courses
|
||||
*/
|
||||
@TableName(value ="courses")
|
||||
@Data
|
||||
public class Courses implements Serializable {
|
||||
/**
|
||||
* 课程ID
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Integer id;
|
||||
|
||||
/**
|
||||
* 课程标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 分类编码
|
||||
*/
|
||||
private String categoryId;
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 封面图片URL
|
||||
*/
|
||||
private String coverImg;
|
||||
|
||||
/**
|
||||
* 学习人数
|
||||
*/
|
||||
private Integer studentCount;
|
||||
|
||||
/**
|
||||
* 评分
|
||||
*/
|
||||
private BigDecimal rating;
|
||||
|
||||
/**
|
||||
* 价格
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
@TableField(exist = false)
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.guwan.backend.pojo.dto.user;
|
||||
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class BSRegisterDTO {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.guwan.backend.service;
|
||||
|
||||
import com.guwan.backend.pojo.dto.BSCategory;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author 12455
|
||||
* @description 针对表【category】的数据库操作Service
|
||||
* @createDate 2025-03-13 23:00:51
|
||||
*/
|
||||
public interface BSCategoryService extends IService<BSCategory> {
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.guwan.backend.service;
|
||||
|
||||
import com.guwan.backend.pojo.Courses;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
|
||||
/**
|
||||
* @author 12455
|
||||
* @description 针对表【courses(课程表)】的数据库操作Service
|
||||
* @createDate 2025-03-13 22:45:19
|
||||
*/
|
||||
public interface CoursesService extends IService<Courses> {
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.guwan.backend.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.guwan.backend.pojo.dto.BSCategory;
|
||||
import com.guwan.backend.service.BSCategoryService;
|
||||
import com.guwan.backend.mapper.BSCategoryMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author 12455
|
||||
* @description 针对表【category】的数据库操作Service实现
|
||||
* @createDate 2025-03-13 23:00:51
|
||||
*/
|
||||
@Service
|
||||
public class BSCategoryServiceImpl extends ServiceImpl<BSCategoryMapper, BSCategory>
|
||||
implements BSCategoryService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.guwan.backend.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.guwan.backend.pojo.Courses;
|
||||
import com.guwan.backend.service.CoursesService;
|
||||
import com.guwan.backend.mapper.CoursesMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author 12455
|
||||
* @description 针对表【courses(课程表)】的数据库操作Service实现
|
||||
* @createDate 2025-03-13 22:45:19
|
||||
*/
|
||||
@Service
|
||||
public class CoursesServiceImpl extends ServiceImpl<CoursesMapper, Courses>
|
||||
implements CoursesService{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.guwan.backend.mapper.BSCategoryMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.dto.BSCategory">
|
||||
<id property="id" column="id" jdbcType="VARCHAR"/>
|
||||
<result property="name" column="name" jdbcType="VARCHAR"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,name
|
||||
</sql>
|
||||
</mapper>
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.guwan.backend.mapper.CoursesMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.guwan.backend.pojo.Courses">
|
||||
<id property="id" column="id" jdbcType="INTEGER"/>
|
||||
<result property="title" column="title" jdbcType="VARCHAR"/>
|
||||
<result property="categoryId" column="category_id" jdbcType="VARCHAR"/>
|
||||
<result property="categoryName" column="category_name" jdbcType="VARCHAR"/>
|
||||
<result property="coverImg" column="cover_img" jdbcType="VARCHAR"/>
|
||||
<result property="studentCount" column="student_count" jdbcType="INTEGER"/>
|
||||
<result property="rating" column="rating" jdbcType="DECIMAL"/>
|
||||
<result property="price" column="price" jdbcType="DECIMAL"/>
|
||||
</resultMap>
|
||||
|
||||
<sql id="Base_Column_List">
|
||||
id,title,category_id,
|
||||
category_name,cover_img,student_count,
|
||||
rating,price
|
||||
</sql>
|
||||
</mapper>
|
Loading…
Reference in New Issue