diff --git a/src/main/java/com/guwan/backend/constant/SecurityConstants.java b/src/main/java/com/guwan/backend/constant/SecurityConstants.java index 7335200..718ff58 100644 --- a/src/main/java/com/guwan/backend/constant/SecurityConstants.java +++ b/src/main/java/com/guwan/backend/constant/SecurityConstants.java @@ -12,6 +12,7 @@ public class SecurityConstants { * 这些路径可以直接访问,不需要认证 */ public static final List WHITE_LIST = List.of( + "/bs/**", "/api/common/**", //公共接口 "/demo/**", // 测试接口 "/api/products", diff --git a/src/main/java/com/guwan/backend/controller/BSUserController.java b/src/main/java/com/guwan/backend/controller/BSUserController.java new file mode 100644 index 0000000..00bec97 --- /dev/null +++ b/src/main/java/com/guwan/backend/controller/BSUserController.java @@ -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 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 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 getCurrentUser() { + UserDTO user = userService.getCurrentUser(); + if (user == null) { + return Result.unauthorized("用户未登录"); + } + return Result.success(user); + } + + @GetMapping("/{id}") + public Result getUserById(@PathVariable Long id) { + UserDTO user = userService.getUserById(id); + if (user == null) { + return Result.notFound("用户不存在"); + } + return Result.success(user); + } + + @PostMapping("/token/refresh") + public Result 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 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 updateUserInfo(@RequestBody @Valid UserDTO userDTO) { + try { + return Result.success(userService.updateUserInfo(userDTO)); + } catch (Exception e) { + log.error("更新用户信息失败", e); + return Result.error(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/controller/CoursesController.java b/src/main/java/com/guwan/backend/controller/CoursesController.java new file mode 100644 index 0000000..8cbbd55 --- /dev/null +++ b/src/main/java/com/guwan/backend/controller/CoursesController.java @@ -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 queryById(@PathVariable("id") Integer id) { + return Result.success(this.coursesService.getById(id)); + } + + /** + * 新增数据 + * + * @param courses 实体 + * @return 新增结果 + */ + @PostMapping + public Result add(Courses courses) { + this.coursesService.save(courses); + return Result.success(); + } + + /** + * 编辑数据 + * + * @param courses 实体 + * @return 编辑结果 + */ + @PutMapping + public Result edit(Courses courses) { + //return Result.success(this.coursesService.update(courses)); + return Result.success(); + } + + /** + * 删除数据 + * + * @param id 主键 + * @return 删除是否成功 + */ + @DeleteMapping + public Result deleteById(Integer id) { + return Result.success(this.coursesService.removeById(id)); + } + +} + diff --git a/src/main/java/com/guwan/backend/controller/FaceController.java b/src/main/java/com/guwan/backend/controller/FaceController.java new file mode 100644 index 0000000..83e9430 --- /dev/null +++ b/src/main/java/com/guwan/backend/controller/FaceController.java @@ -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 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; + } + +} +*/ diff --git a/src/main/java/com/guwan/backend/face/FaceEngineAutoRun.java b/src/main/java/com/guwan/backend/face/FaceEngineAutoRun.java new file mode 100644 index 0000000..7abb09c --- /dev/null +++ b/src/main/java/com/guwan/backend/face/FaceEngineAutoRun.java @@ -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 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 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 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 cellList = tenCellService.list(new QueryWrapper() + .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 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+" 人"); + } + } +} +*/ diff --git a/src/main/java/com/guwan/backend/face/config/ArcFaceAutoConfiguration.java b/src/main/java/com/guwan/backend/face/config/ArcFaceAutoConfiguration.java new file mode 100644 index 0000000..c65bdd0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/config/ArcFaceAutoConfiguration.java @@ -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 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 { + } + +} diff --git a/src/main/java/com/guwan/backend/face/dto/CompareFacesReqDTO.java b/src/main/java/com/guwan/backend/face/dto/CompareFacesReqDTO.java new file mode 100644 index 0000000..5f474a9 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/CompareFacesReqDTO.java @@ -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; +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceAddReqDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceAddReqDTO.java new file mode 100644 index 0000000..6ba4b17 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceAddReqDTO.java @@ -0,0 +1,12 @@ +package com.guwan.backend.face.dto; + +import lombok.Data; + +@Data +public class FaceAddReqDTO { + + private String name; + + private String image; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceDetectReqDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceDetectReqDTO.java new file mode 100644 index 0000000..e8dbff0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceDetectReqDTO.java @@ -0,0 +1,10 @@ +package com.guwan.backend.face.dto; + +import lombok.Data; + +@Data +public class FaceDetectReqDTO { + + private String image; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceDetectResDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceDetectResDTO.java new file mode 100644 index 0000000..1f94ed7 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceDetectResDTO.java @@ -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; +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceRecognitionReqDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionReqDTO.java new file mode 100644 index 0000000..de0b1dc --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionReqDTO.java @@ -0,0 +1,10 @@ +package com.guwan.backend.face.dto; + +import lombok.Data; + +@Data +public class FaceRecognitionReqDTO { + + private String image; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceRecognitionResDTO.java b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionResDTO.java new file mode 100644 index 0000000..ee9f031 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceRecognitionResDTO.java @@ -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; + +} diff --git a/src/main/java/com/guwan/backend/face/dto/FaceVideoVo.java b/src/main/java/com/guwan/backend/face/dto/FaceVideoVo.java new file mode 100644 index 0000000..854e1de --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/FaceVideoVo.java @@ -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; + + + +} diff --git a/src/main/java/com/guwan/backend/face/dto/GetFaceListResDTO.java b/src/main/java/com/guwan/backend/face/dto/GetFaceListResDTO.java new file mode 100644 index 0000000..7130ddb --- /dev/null +++ b/src/main/java/com/guwan/backend/face/dto/GetFaceListResDTO.java @@ -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; + +} diff --git a/src/main/java/com/guwan/backend/face/entity/ProcessInfo.java b/src/main/java/com/guwan/backend/face/entity/ProcessInfo.java new file mode 100644 index 0000000..dac68fa --- /dev/null +++ b/src/main/java/com/guwan/backend/face/entity/ProcessInfo.java @@ -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; + +} diff --git a/src/main/java/com/guwan/backend/face/entity/UserCompareInfo.java b/src/main/java/com/guwan/backend/face/entity/UserCompareInfo.java new file mode 100644 index 0000000..938a01a --- /dev/null +++ b/src/main/java/com/guwan/backend/face/entity/UserCompareInfo.java @@ -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是 +} diff --git a/src/main/java/com/guwan/backend/face/enums/ErrorCodeEnum.java b/src/main/java/com/guwan/backend/face/enums/ErrorCodeEnum.java new file mode 100644 index 0000000..e8c76e8 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/enums/ErrorCodeEnum.java @@ -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; + } + +} diff --git a/src/main/java/com/guwan/backend/face/face/FacePreviewInfo.java b/src/main/java/com/guwan/backend/face/face/FacePreviewInfo.java new file mode 100644 index 0000000..2e8dd88 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/face/FacePreviewInfo.java @@ -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; + } + +} diff --git a/src/main/java/com/guwan/backend/face/face/FaceRecognize.java b/src/main/java/com/guwan/backend/face/face/FaceRecognize.java new file mode 100644 index 0000000..93edc13 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/face/FaceRecognize.java @@ -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 frEnginePool; + + + private static volatile ConcurrentHashMap faceResultRegistry = new ConcurrentHashMap<>(); + + private static ExecutorService frService = Executors.newFixedThreadPool(20); + + public static ConcurrentHashMap 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 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 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 detectFaces(ImageInfo imageInfo) { + if (ftEngine != null) { + List faceInfoList = new ArrayList<>(); + int code = ftEngine.detectFaces(imageInfo.getImageData(), imageInfo.getWidth(), imageInfo.getHeight(), + imageInfo.getImageFormat(), faceInfoList); + + List 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 faceInfoList) { + if (System.currentTimeMillis() - lastClearTime > 5000) { + Iterator 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> iterator = faceFeatureRegistry.entrySet().iterator(); + for (; iterator.hasNext(); ) { + Map.Entry 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); + } + } + + + } + } + + +} diff --git a/src/main/java/com/guwan/backend/face/factory/FaceEngineFactory.java b/src/main/java/com/guwan/backend/face/factory/FaceEngineFactory.java new file mode 100644 index 0000000..4eed0b3 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/factory/FaceEngineFactory.java @@ -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 { + + 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 wrap(FaceEngine faceEngine) { + return new DefaultPooledObject<>(faceEngine); + } + + + @Override + public void destroyObject(PooledObject p) throws Exception { + FaceEngine faceEngine = p.getObject(); + int result = faceEngine.unInit(); + super.destroyObject(p); + } +} diff --git a/src/main/java/com/guwan/backend/face/rpc/BusinessException.java b/src/main/java/com/guwan/backend/face/rpc/BusinessException.java new file mode 100644 index 0000000..efad3b0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/BusinessException.java @@ -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; + } +} diff --git a/src/main/java/com/guwan/backend/face/rpc/ErrorCode.java b/src/main/java/com/guwan/backend/face/rpc/ErrorCode.java new file mode 100644 index 0000000..ceb2dec --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/ErrorCode.java @@ -0,0 +1,12 @@ +package com.guwan.backend.face.rpc; + +public interface ErrorCode { + + + Integer getCode(); + + String getDesc(); + + String getDescCN(); + +} diff --git a/src/main/java/com/guwan/backend/face/rpc/GlobalExceptionHandler.java b/src/main/java/com/guwan/backend/face/rpc/GlobalExceptionHandler.java new file mode 100644 index 0000000..3995898 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/GlobalExceptionHandler.java @@ -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; + } + + + + +} diff --git a/src/main/java/com/guwan/backend/face/rpc/Response.java b/src/main/java/com/guwan/backend/face/rpc/Response.java new file mode 100644 index 0000000..2b4ac76 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rpc/Response.java @@ -0,0 +1,39 @@ +package com.guwan.backend.face.rpc; + +import lombok.Data; + +@Data +public class Response { + + private int code = -1; + private String msg = "success"; + private T data; + + public static Response newSuccessResponse(T data) { + return newResponse(data, 0, "success"); + } + + public static Response newFailedResponse(Integer code, String message) { + return newResponse(null, code, message); + } + + public static Response newFailedResponse(ErrorCode ErrorCode) { + return newResponse(null, ErrorCode.getCode(), ErrorCode.getDesc()); + } + + public static Response newFailedResponse(ErrorCode ErrorCode, String message) { + return newResponse(null, ErrorCode.getCode(), message); + } + + public static Response newResponse(T data, Integer code, String message) { + Response response = new Response(); + response.setCode(code); + response.setMsg(message); + if (data != null && data instanceof String && "".equals(data)) { + response.setData(null); + } else { + response.setData(data); + } + return response; + } +} diff --git a/src/main/java/com/guwan/backend/face/rtsp/RtspFrameGrabber.java b/src/main/java/com/guwan/backend/face/rtsp/RtspFrameGrabber.java new file mode 100644 index 0000000..ebd9a67 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/rtsp/RtspFrameGrabber.java @@ -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 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 startGrabber(String cellId) { + + List 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 recognition(MultipartFile file,String cellId) { + try { + byte[] bytes = file.getBytes(); + return imageRecognition(bytes, cellId); + + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private List imageRecognition(byte[] bytes,String cellId) { + List temp = new ArrayList<>(); + if (bytes != null && bytes.length > 0) { + ImageInfo imageInfo = ImageFactory.getRGBData(bytes); + List 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 userInfoList = UserRamGroup.getUserList(cellId); + //人脸对比 这里长度永远为1 + List 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() + .eq(TenPersonEntity::getPersonId, userCompareInfo.getFaceId())); + Map 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 params = new HashMap<>(); + params.put("pmWatchVideoRecordId", ""); + params.put("orgId", tenPerson.getOrgId()); + params.put("orgName", tenCellDao.selectOne(new QueryWrapper().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() + .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(); + } + +} +*/ diff --git a/src/main/java/com/guwan/backend/face/service/FaceEngineService.java b/src/main/java/com/guwan/backend/face/service/FaceEngineService.java new file mode 100644 index 0000000..b694dd0 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/service/FaceEngineService.java @@ -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 detectFaces(ImageInfo imageInfo); + + Float compareFace(ImageInfo imageInfo1, ImageInfo imageInfo2) ; + + byte[] extractFaceFeature(ImageInfo imageInfo, FaceInfo faceInfo, ExtractType extractType); + + List faceRecognition(byte[] faceFeature, List userInfoList, float passRate) ; + + List process(ImageInfo imageInfo, List faceInfoList); + + + +} diff --git a/src/main/java/com/guwan/backend/face/service/impl/FaceEngineServiceImpl.java b/src/main/java/com/guwan/backend/face/service/impl/FaceEngineServiceImpl.java new file mode 100644 index 0000000..f6ef7d6 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/service/impl/FaceEngineServiceImpl.java @@ -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 faceEngineGeneralPool; + + //人脸比对引擎池 + private GenericObjectPool 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 detectFaces(ImageInfo imageInfo) { + + FaceEngine faceEngine = null; + try { + faceEngine = faceEngineGeneralPool.borrowObject(); + if (faceEngine == null) { + throw new BusinessException(ErrorCodeEnum.FAIL, "获取引擎失败"); + } + + //人脸检测得到人脸列表 + List 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 faceInfoList1 = detectFaces(imageInfo1); + List 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 faceRecognition(byte[] faceFeature, List userInfoList, float passRate) { + List resultUserInfoList = Lists.newLinkedList();//识别到的人脸列表 + + FaceFeature targetFaceFeature = new FaceFeature(); + targetFaceFeature.setFeatureData(faceFeature); + + List> faceUserInfoPartList = Lists.partition(userInfoList, 1000);//分成1000一组,多线程处理 + CompletionService> completionService = new ExecutorCompletionService(compareExecutorService); + for (List part : faceUserInfoPartList) { + completionService.submit(new CompareFaceTask(part, targetFaceFeature, passRate)); + } + for (int i = 0; i < faceUserInfoPartList.size(); i++) { + List 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 process(ImageInfo imageInfo, List 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 processInfoList = Lists.newLinkedList(); + + //性别列表 + List genderInfoList = new ArrayList(); + faceEngine.getGender(genderInfoList); + + //年龄列表 + List ageInfoList = new ArrayList(); + faceEngine.getAge(ageInfoList); + //活体结果列表 + List livenessInfoList = new ArrayList(); + 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> { + + private List userInfoList; + private FaceFeature targetFaceFeature; + private float passRate; + + + public CompareFaceTask(List userInfoList, FaceFeature targetFaceFeature, float passRate) { + this.userInfoList = userInfoList; + this.targetFaceFeature = targetFaceFeature; + this.passRate = passRate; + } + + @Override + public List call() throws Exception { + FaceEngine faceEngine = null; + List 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; + } + + } +} diff --git a/src/main/java/com/guwan/backend/face/util/Base64Util.java b/src/main/java/com/guwan/backend/face/util/Base64Util.java new file mode 100644 index 0000000..7b94ab7 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/Base64Util.java @@ -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; + + + } +} diff --git a/src/main/java/com/guwan/backend/face/util/FaceEngineTest.java b/src/main/java/com/guwan/backend/face/util/FaceEngineTest.java new file mode 100644 index 0000000..532cf55 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/FaceEngineTest.java @@ -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 faceInfoList = new ArrayList(); + 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 faceInfoList2 = new ArrayList(); + 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 genderInfoList = new ArrayList(); + errorCode = faceEngine.getGender(genderInfoList); + System.out.println("性别:" + genderInfoList.get(0).getGender()); + + //年龄检测 + List ageInfoList = new ArrayList(); + errorCode = faceEngine.getAge(ageInfoList); + System.out.println("年龄:" + ageInfoList.get(0).getAge()); + + //活体检测 + List livenessInfoList = new ArrayList(); + errorCode = faceEngine.getLiveness(livenessInfoList); + System.out.println("活体:" + livenessInfoList.get(0).getLiveness()); + + //口罩检测 + List maskInfoList = new ArrayList(); + errorCode = faceEngine.getMask(maskInfoList); + System.out.println("口罩:" + maskInfoList.get(0).getMask()); + + + //IR属性处理 + ImageInfo imageInfoGray = ImageFactory.getGrayData(new File("/home/huangyifang/gb/咸阳师范/10.jpg")); + List faceInfoListGray = new ArrayList(); + errorCode = faceEngine.detectFaces(imageInfoGray, faceInfoListGray); + + FunctionConfiguration configuration2 = new FunctionConfiguration(); + configuration2.setSupportIRLiveness(true); + errorCode = faceEngine.processIr(imageInfoGray, faceInfoListGray, configuration2); + //IR活体检测 + List 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 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 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(); + + + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/face/util/UserInfo.java b/src/main/java/com/guwan/backend/face/util/UserInfo.java new file mode 100644 index 0000000..23e4fc7 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/UserInfo.java @@ -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; +} diff --git a/src/main/java/com/guwan/backend/face/util/UserRamCache.java b/src/main/java/com/guwan/backend/face/util/UserRamCache.java new file mode 100644 index 0000000..e7ccfbf --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/UserRamCache.java @@ -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 USER_INFO_MAP = new ConcurrentHashMap<>(); + + private final Set 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 getUserList() { + List 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) { + + } + } +} diff --git a/src/main/java/com/guwan/backend/face/util/UserRamGroup.java b/src/main/java/com/guwan/backend/face/util/UserRamGroup.java new file mode 100644 index 0000000..d7ab8e6 --- /dev/null +++ b/src/main/java/com/guwan/backend/face/util/UserRamGroup.java @@ -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 USER_RAM_GROUP_MAP = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap 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 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(); + } +} diff --git a/src/main/java/com/guwan/backend/mapper/BSCategoryMapper.java b/src/main/java/com/guwan/backend/mapper/BSCategoryMapper.java new file mode 100644 index 0000000..e3f4358 --- /dev/null +++ b/src/main/java/com/guwan/backend/mapper/BSCategoryMapper.java @@ -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 { + +} + + + + diff --git a/src/main/java/com/guwan/backend/mapper/CoursesMapper.java b/src/main/java/com/guwan/backend/mapper/CoursesMapper.java new file mode 100644 index 0000000..de97391 --- /dev/null +++ b/src/main/java/com/guwan/backend/mapper/CoursesMapper.java @@ -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 { + +} + + + + diff --git a/src/main/java/com/guwan/backend/pojo/Courses.java b/src/main/java/com/guwan/backend/pojo/Courses.java new file mode 100644 index 0000000..d678585 --- /dev/null +++ b/src/main/java/com/guwan/backend/pojo/Courses.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/pojo/dto/BSCategory.java b/src/main/java/com/guwan/backend/pojo/dto/BSCategory.java new file mode 100644 index 0000000..750af14 --- /dev/null +++ b/src/main/java/com/guwan/backend/pojo/dto/BSCategory.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/pojo/dto/Courses.java b/src/main/java/com/guwan/backend/pojo/dto/Courses.java new file mode 100644 index 0000000..8b6563a --- /dev/null +++ b/src/main/java/com/guwan/backend/pojo/dto/Courses.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/pojo/dto/user/BSRegisterDTO.java b/src/main/java/com/guwan/backend/pojo/dto/user/BSRegisterDTO.java new file mode 100644 index 0000000..f338e35 --- /dev/null +++ b/src/main/java/com/guwan/backend/pojo/dto/user/BSRegisterDTO.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/service/BSCategoryService.java b/src/main/java/com/guwan/backend/service/BSCategoryService.java new file mode 100644 index 0000000..c326c8a --- /dev/null +++ b/src/main/java/com/guwan/backend/service/BSCategoryService.java @@ -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 { + +} diff --git a/src/main/java/com/guwan/backend/service/CoursesService.java b/src/main/java/com/guwan/backend/service/CoursesService.java new file mode 100644 index 0000000..483a0a1 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/CoursesService.java @@ -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 { + +} diff --git a/src/main/java/com/guwan/backend/service/impl/BSCategoryServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/BSCategoryServiceImpl.java new file mode 100644 index 0000000..06a1fe7 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/impl/BSCategoryServiceImpl.java @@ -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 + implements BSCategoryService{ + +} + + + + diff --git a/src/main/java/com/guwan/backend/service/impl/CoursesServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/CoursesServiceImpl.java new file mode 100644 index 0000000..d0752dc --- /dev/null +++ b/src/main/java/com/guwan/backend/service/impl/CoursesServiceImpl.java @@ -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 + implements CoursesService{ + +} + + + + diff --git a/src/main/resources/mapper/BSCategoryMapper.xml b/src/main/resources/mapper/BSCategoryMapper.xml new file mode 100644 index 0000000..5d3090e --- /dev/null +++ b/src/main/resources/mapper/BSCategoryMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + id,name + + diff --git a/src/main/resources/mapper/CoursesMapper.xml b/src/main/resources/mapper/CoursesMapper.xml new file mode 100644 index 0000000..baf34fa --- /dev/null +++ b/src/main/resources/mapper/CoursesMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + id,title,category_id, + category_name,cover_img,student_count, + rating,price + +