人脸识别与大模型问答
This commit is contained in:
parent
a8b9bde316
commit
8a78b494fb
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-modules</artifactId>
|
||||
<version>24.7.1-SNAPSHOT</version>
|
||||
<version>24.7.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
|
@ -55,6 +55,11 @@
|
|||
<artifactId>bonus-common-datasource</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
<artifactId>bonus-common-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- bonus Common DataScope -->
|
||||
<dependency>
|
||||
<groupId>com.bonus</groupId>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package com.bonus.ai.controller;
|
||||
|
||||
import com.bonus.ai.domain.vo.FaceVo;
|
||||
import com.bonus.ai.service.FaceService;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* 人脸识别控制层
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/face")
|
||||
@Slf4j
|
||||
public class FaceController {
|
||||
|
||||
@Resource
|
||||
private FaceService faceService;
|
||||
|
||||
|
||||
@PostMapping("/getList")
|
||||
public AjaxResult getList() {
|
||||
return faceService.getList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸上传
|
||||
*
|
||||
* @param file 人脸图片
|
||||
* @param faceVo 人脸信息
|
||||
* @return 插入是否成功
|
||||
*/
|
||||
|
||||
@PostMapping("/upload")
|
||||
public AjaxResult upload(MultipartFile file, FaceVo faceVo) {
|
||||
return faceService.insertFace(file, faceVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸识别
|
||||
*
|
||||
* @param file 人脸图片
|
||||
* @return 人脸信息
|
||||
*/
|
||||
@PostMapping("/recognition")
|
||||
public AjaxResult recognition(MultipartFile file) {
|
||||
return faceService.recognition(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别结果统计
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping("/resultStatistics")
|
||||
public AjaxResult resultStatistics() {
|
||||
return faceService.resultStatistics();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package com.bonus.ai.controller;
|
||||
|
||||
import com.bonus.ai.domain.KnowledgeChatWindowVo;
|
||||
import com.bonus.ai.domain.vo.AiQuestionAnswer;
|
||||
import com.bonus.ai.service.KnowledgeService;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/knowledge")
|
||||
@Slf4j
|
||||
public class KnowledgeController {
|
||||
|
||||
@Resource
|
||||
private KnowledgeService knowledgeService;
|
||||
|
||||
/**
|
||||
* 获取窗口列表
|
||||
*
|
||||
* @return 集合
|
||||
*/
|
||||
@PostMapping("/getList")
|
||||
public AjaxResult getList() {
|
||||
return knowledgeService.getList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增窗口
|
||||
*
|
||||
* @return 集合
|
||||
*/
|
||||
@PostMapping("/insertChatWindow")
|
||||
public AjaxResult insertChatWindow(@RequestBody KnowledgeChatWindowVo knowledgeChatWindowVo) {
|
||||
return knowledgeService.insertChatWindow(knowledgeChatWindowVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新聊天窗口信息
|
||||
*
|
||||
* @param knowledgeChatWindowVo 聊天窗口信息
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@PostMapping("/updateChatWindow")
|
||||
public AjaxResult updateChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo) {
|
||||
return knowledgeService.updateChatWindow(knowledgeChatWindowVo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑删除聊天窗口
|
||||
*
|
||||
* @param windowId 窗口ID
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@PostMapping("/deleteChatWindow/{windowId}")
|
||||
public AjaxResult deleteChatWindow(@PathVariable Long windowId) {
|
||||
return knowledgeService.deleteChatWindow(windowId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新聊天窗口信息
|
||||
*
|
||||
* @param windowId 聊天窗口信息
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@PostMapping("/getAllByWindowId/{windowId}")
|
||||
public AjaxResult getAllByWindowId(@PathVariable Long windowId) {
|
||||
return knowledgeService.getAllByWindowId(windowId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑删除聊天窗口
|
||||
*
|
||||
* @param aiQuestionAnswer 问答记录
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@PostMapping("/insertQuestionAnswer")
|
||||
public AjaxResult insertQuestionAnswer(AiQuestionAnswer aiQuestionAnswer) {
|
||||
return knowledgeService.insertQuestionAnswer(aiQuestionAnswer);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.bonus.ai.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* 问答窗口实体类
|
||||
*/
|
||||
@Data
|
||||
public class KnowledgeChatWindowVo {
|
||||
/**
|
||||
* 窗口id
|
||||
*/
|
||||
private Long windowId;
|
||||
/**
|
||||
* 知识库id
|
||||
*/
|
||||
private String knowledgeId;
|
||||
/**
|
||||
* 窗口名称
|
||||
*/
|
||||
private String windowName;
|
||||
/**
|
||||
* 聊天类型
|
||||
*/
|
||||
private String chatType;
|
||||
/**
|
||||
* 客户
|
||||
*/
|
||||
private Long customerId;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private String delFlag;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.bonus.ai.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Data
|
||||
public class AiQuestionAnswer {
|
||||
/**
|
||||
* 问答记录ID,主键,自增
|
||||
*/
|
||||
private Long recordId;
|
||||
/**
|
||||
* 窗口ID,可为空
|
||||
*/
|
||||
private Long windowId;
|
||||
/**
|
||||
* 问题内容,使用长文本
|
||||
*/
|
||||
private String question;
|
||||
/**
|
||||
* 回答内容,使用长文本
|
||||
*/
|
||||
private String answer;
|
||||
/**
|
||||
* 知识库溯源文档,使用长文本
|
||||
*/
|
||||
private String knowledge;
|
||||
/**
|
||||
* 删除标志,0代表存在,2代表删除
|
||||
*/
|
||||
private String delFlag;
|
||||
/**
|
||||
* 客户ID,不可为空
|
||||
*/
|
||||
private String customerId;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 备注,最大长度500
|
||||
*/
|
||||
private String remark;
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.bonus.ai.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* 人脸识别调用实体类
|
||||
*/
|
||||
@Data
|
||||
public class FaceResultVo implements Serializable {
|
||||
/**
|
||||
* 结果主键id
|
||||
*/
|
||||
private String resultId;
|
||||
/**
|
||||
* 人脸库表id
|
||||
*/
|
||||
private String faceId;
|
||||
/**
|
||||
* 服务id
|
||||
*/
|
||||
private String serviceId;
|
||||
/**
|
||||
* 识别结果
|
||||
*/
|
||||
private String resultIt;
|
||||
/**
|
||||
* 人脸路径
|
||||
*/
|
||||
private String faceAddress;
|
||||
/**
|
||||
* 识别时间
|
||||
*/
|
||||
private String recognizeTime;
|
||||
/**
|
||||
* 响应时长
|
||||
*/
|
||||
private String responseLong;
|
||||
/**
|
||||
* 调用服务IP
|
||||
*/
|
||||
private String invokeIp;
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private String updateBy;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private String updateTime;
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private String delFlag;
|
||||
/**
|
||||
* 结果类型(0用户登录,1服务注册)
|
||||
*/
|
||||
private String resultType;
|
||||
/**
|
||||
* 识别状态(0成功,1失败)
|
||||
*/
|
||||
private String resultStatus;
|
||||
/**
|
||||
* 识别失败原因
|
||||
*/
|
||||
private String failureReason;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.bonus.ai.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* 人脸识别实体类
|
||||
*/
|
||||
@Data
|
||||
public class FaceVo implements Serializable {
|
||||
/**
|
||||
* 人脸id
|
||||
*/
|
||||
private Long faceId;
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 性别
|
||||
*/
|
||||
private String sex;
|
||||
/**
|
||||
* 电话号码
|
||||
*/
|
||||
private String phone;
|
||||
/**
|
||||
* 身份证号
|
||||
*/
|
||||
private String idCardNumber;
|
||||
/**
|
||||
* 人脸图片路径
|
||||
*/
|
||||
private String faceAddress;
|
||||
/**
|
||||
* 人脸特征
|
||||
*/
|
||||
private byte[] feature;
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private String updateBy;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 是否删除
|
||||
*/
|
||||
private String delFlag;
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.bonus.ai.mapper;
|
||||
|
||||
import com.bonus.ai.domain.vo.FaceResultVo;
|
||||
import com.bonus.ai.domain.vo.FaceVo;
|
||||
import feign.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* 人脸识别mapper层
|
||||
*/
|
||||
@Repository
|
||||
public interface FaceMapper {
|
||||
/**
|
||||
* 查询全部人脸信息
|
||||
*/
|
||||
List<FaceVo> getList();
|
||||
|
||||
/**
|
||||
* 根据 faceId 判断人脸是否存在
|
||||
*
|
||||
* @param faceId 人脸唯一标识
|
||||
* @return 返回 1 表示存在,0 表示不存在
|
||||
*/
|
||||
int existsByFaceId(@Param("faceId") String faceId);
|
||||
|
||||
/**
|
||||
* 插入人脸信息
|
||||
*
|
||||
* @param face 人脸信息对象
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
int insertFace(FaceVo face);
|
||||
|
||||
/**
|
||||
* 通过身份证id查询人员
|
||||
*
|
||||
* @param idCardNumber 身份证id
|
||||
* @return 人员信息
|
||||
*/
|
||||
List<FaceVo> getFaceByIdCardNumber(String idCardNumber);
|
||||
|
||||
/**
|
||||
* 插入人脸识别记录
|
||||
*
|
||||
* @param faceResultVo 人脸信息对象
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
int insertFaceResult(FaceResultVo faceResultVo);
|
||||
|
||||
/**
|
||||
* 识别结果统计
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
List<FaceResultVo> resultStatistics();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.bonus.ai.mapper;
|
||||
|
||||
import com.bonus.ai.domain.KnowledgeChatWindowVo;
|
||||
import com.bonus.ai.domain.vo.AiQuestionAnswer;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mapper接口,用于操作聊天窗口数据。
|
||||
* 提供获取窗口列表、新增、更新、删除窗口的方法。
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
@Repository
|
||||
public interface KnowledgeMapper {
|
||||
|
||||
/**
|
||||
* 获取窗口列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 包含聊天窗口信息的集合
|
||||
*/
|
||||
List<KnowledgeChatWindowVo> getList(Long userId);
|
||||
|
||||
/**
|
||||
* 新增窗口
|
||||
*
|
||||
* @param knowledgeChatWindowVo 聊天窗口信息
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
int insertChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo);
|
||||
|
||||
/**
|
||||
* 更新聊天窗口信息
|
||||
*
|
||||
* @param knowledgeChatWindowVo 聊天窗口信息
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
int updateChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo);
|
||||
|
||||
/**
|
||||
* 逻辑删除聊天窗口
|
||||
*
|
||||
* @param windowId 窗口ID
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
int deleteChatWindow(Long windowId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据窗口id获取问答记录
|
||||
*
|
||||
* @return 所有问答记录的列表
|
||||
*/
|
||||
List<AiQuestionAnswer> getAllByWindowId(Long windowId);
|
||||
|
||||
/**
|
||||
* 插入问答信息
|
||||
*
|
||||
* @param aiQuestionAnswer 问答信息
|
||||
* @return 生成的窗口ID
|
||||
*/
|
||||
Long insertQuestionAnswer(AiQuestionAnswer aiQuestionAnswer);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package com.bonus.ai.service;
|
||||
|
||||
import com.bonus.ai.domain.vo.FaceVo;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import feign.Param;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
public interface FaceService {
|
||||
|
||||
/**
|
||||
* 查询全部人脸信息
|
||||
*/
|
||||
AjaxResult getList();
|
||||
|
||||
/**
|
||||
* 插入人脸信息
|
||||
*
|
||||
* @param face 人脸信息对象
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
AjaxResult insertFace(MultipartFile file, FaceVo face);
|
||||
|
||||
/**
|
||||
* 人脸识别
|
||||
*
|
||||
* @param file 人脸图片
|
||||
* @return 返回人脸信息
|
||||
*/
|
||||
AjaxResult recognition(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 识别结果统计
|
||||
* @return 结果
|
||||
*/
|
||||
AjaxResult resultStatistics();
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
package com.bonus.ai.service;
|
||||
|
||||
import com.bonus.ai.domain.KnowledgeChatWindowVo;
|
||||
import com.bonus.ai.domain.vo.AiQuestionAnswer;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
public interface KnowledgeService {
|
||||
/**
|
||||
* 获取窗口列表
|
||||
*
|
||||
* @return 集合
|
||||
*/
|
||||
AjaxResult getList();
|
||||
|
||||
/**
|
||||
* 新增窗口
|
||||
*
|
||||
* @return 集合
|
||||
*/
|
||||
AjaxResult insertChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo);
|
||||
|
||||
|
||||
/**
|
||||
* 更新聊天窗口信息
|
||||
*
|
||||
* @param knowledgeChatWindowVo 聊天窗口信息
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
AjaxResult updateChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo);
|
||||
|
||||
/**
|
||||
* 逻辑删除聊天窗口
|
||||
*
|
||||
* @param windowId 窗口ID
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
AjaxResult deleteChatWindow(Long windowId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据窗口id获取问答记录
|
||||
*
|
||||
* @return 所有问答记录的列表
|
||||
*/
|
||||
AjaxResult getAllByWindowId(Long windowId);
|
||||
|
||||
/**
|
||||
* 插入问答信息
|
||||
*
|
||||
* @param aiQuestionAnswer 问答信息
|
||||
* @return 生成的窗口ID
|
||||
*/
|
||||
AjaxResult insertQuestionAnswer(AiQuestionAnswer aiQuestionAnswer);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
package com.bonus.ai.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.bonus.ai.domain.vo.FaceVo;
|
||||
import com.bonus.ai.mapper.FaceMapper;
|
||||
import com.bonus.ai.service.FaceService;
|
||||
import com.bonus.ai.utils.FaceUtils;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.security.utils.SecurityUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
* 人脸识别服务层
|
||||
*/
|
||||
@Service
|
||||
public class FaceServiceImpl implements FaceService {
|
||||
|
||||
@Resource
|
||||
private FaceMapper faceMapper;
|
||||
|
||||
/**
|
||||
* 查询全部人脸信息
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult getList() {
|
||||
return AjaxResult.success(faceMapper.getList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入人脸信息
|
||||
*
|
||||
* @param face 人脸信息对象
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult insertFace(MultipartFile file, FaceVo face) {
|
||||
if (isFileOrFaceInvalid(file, face)) {
|
||||
return AjaxResult.error("文件或人员信息不能为空, 请检查输入");
|
||||
}
|
||||
try {
|
||||
String base64WithMimeType = getBase64WithMimeType(file);
|
||||
String response = FaceUtils.updateDb(base64WithMimeType, "add", face.getIdCardNumber());
|
||||
return handleInsertResponse(file, response, face, base64WithMimeType);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("插入人脸信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 人脸识别
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult recognition(MultipartFile file) {
|
||||
if (ObjectUtils.isEmpty(file)) {
|
||||
return AjaxResult.error("文件不能为空, 请选择文件");
|
||||
}
|
||||
|
||||
try {
|
||||
String base64WithMimeType = getBase64WithMimeType(file);
|
||||
String response = FaceUtils.faceRecognition(base64WithMimeType);
|
||||
return handleRecognitionResponse(response);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error("人脸识别失败");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 识别结果统计
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult resultStatistics() {
|
||||
return AjaxResult.success(faceMapper.resultStatistics());
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件的 MIME 类型是否为图片类型
|
||||
*/
|
||||
private boolean isImageMimeType(String mimeType) {
|
||||
Set<String> validMimeTypes = new HashSet<>();
|
||||
validMimeTypes.add("image/jpeg");
|
||||
validMimeTypes.add("image/png");
|
||||
return mimeType != null && validMimeTypes.contains(mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件和人员信息是否为空
|
||||
*/
|
||||
private boolean isFileOrFaceInvalid(MultipartFile file, FaceVo face) {
|
||||
return ObjectUtils.isEmpty(file) ||
|
||||
ObjectUtils.isEmpty(face.getName()) ||
|
||||
ObjectUtils.isEmpty(face.getIdCardNumber());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Base64编码的文件内容,并添加MIME类型
|
||||
*/
|
||||
private String getBase64WithMimeType(MultipartFile file) throws Exception {
|
||||
String mimeType = file.getContentType();
|
||||
if (!isImageMimeType(mimeType)) {
|
||||
throw new IllegalArgumentException("文件类型不支持,请上传图片文件");
|
||||
}
|
||||
|
||||
byte[] fileBytes = file.getBytes();
|
||||
String base64String = Base64.getEncoder().encodeToString(fileBytes);
|
||||
return "data:" + mimeType + ";base64," + base64String;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理插入人脸信息的响应
|
||||
*/
|
||||
private AjaxResult handleInsertResponse(MultipartFile file, String response, FaceVo face, String base64WithMimeType) {
|
||||
if (response == null) return AjaxResult.error();
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
String code = jsonObject.getString("code");
|
||||
switch (code) {
|
||||
case "30002":
|
||||
face.setCreateTime(new Date());
|
||||
face.setCreateBy(SecurityUtils.getUsername());
|
||||
face.setUpdateBy(SecurityUtils.getUsername());
|
||||
face.setUpdateTime(new Date());
|
||||
try {
|
||||
// 设置保存文件的目录,这里假设是在应用程序的根目录下的uploads文件夹
|
||||
String uploadDir = "uploads";
|
||||
Path uploadPath = Paths.get(uploadDir);
|
||||
// 如果目录不存在,则创建
|
||||
if (!Files.exists(uploadPath)) {
|
||||
Files.createDirectories(uploadPath);
|
||||
}
|
||||
byte[] bytes = file.getBytes();
|
||||
String fileName = UUID.randomUUID() + file.getOriginalFilename();
|
||||
String filePath = Paths.get("uploads").toAbsolutePath().normalize().toString() + File.separator + fileName;
|
||||
java.nio.file.Files.write(java.nio.file.Paths.get(filePath), bytes);
|
||||
face.setFaceAddress(filePath);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
return faceMapper.insertFace(face) > 0 ? AjaxResult.success() : AjaxResult.error();
|
||||
case "30006":
|
||||
return AjaxResult.error("文件类型不支持");
|
||||
case "30007":
|
||||
return AjaxResult.error("不支持的操作类型");
|
||||
case "30008":
|
||||
return AjaxResult.error("存在多张人脸");
|
||||
case "30009":
|
||||
return AjaxResult.error("光照条件差");
|
||||
case "30010":
|
||||
case "30011":
|
||||
return AjaxResult.error("人脸不全");
|
||||
case "30019":
|
||||
return AjaxResult.error("人员信息已存在");
|
||||
case "30018":
|
||||
return AjaxResult.error("未检测到人脸");
|
||||
default:
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理人脸识别的响应
|
||||
*/
|
||||
private AjaxResult handleRecognitionResponse(String response) {
|
||||
if (response == null) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
JSONObject jsonObject = JSON.parseObject(response);
|
||||
String code = jsonObject.getString("code");
|
||||
switch (code) {
|
||||
case "30000":
|
||||
JSONArray dataArray = jsonObject.getJSONArray("data");
|
||||
String idCardNumber = dataArray.getString(0);
|
||||
List<FaceVo> faceByIdCardNumber = faceMapper.getFaceByIdCardNumber(idCardNumber);
|
||||
return faceByIdCardNumber != null && !faceByIdCardNumber.isEmpty()
|
||||
? AjaxResult.success(faceByIdCardNumber.get(0))
|
||||
: AjaxResult.error("人员不存在");
|
||||
case "30001":
|
||||
return AjaxResult.error("人员不存在");
|
||||
case "30006":
|
||||
return AjaxResult.error("文件类型不支持");
|
||||
case "30007":
|
||||
return AjaxResult.error("不支持的操作类型");
|
||||
case "30008":
|
||||
return AjaxResult.error("存在多张人脸");
|
||||
case "30009":
|
||||
return AjaxResult.error("光照条件差");
|
||||
case "30010":
|
||||
case "30011":
|
||||
return AjaxResult.error("人脸不全");
|
||||
case "30019":
|
||||
return AjaxResult.error("人员信息已存在");
|
||||
case "30018":
|
||||
return AjaxResult.error("未检测到人脸");
|
||||
default:
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package com.bonus.ai.service.impl;
|
||||
|
||||
import com.bonus.ai.domain.KnowledgeChatWindowVo;
|
||||
import com.bonus.ai.domain.vo.AiQuestionAnswer;
|
||||
import com.bonus.ai.mapper.KnowledgeMapper;
|
||||
import com.bonus.ai.service.KnowledgeService;
|
||||
import com.bonus.common.core.web.domain.AjaxResult;
|
||||
import com.bonus.common.security.utils.SecurityUtils;
|
||||
import io.swagger.models.auth.In;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author bonus
|
||||
*/
|
||||
@Service
|
||||
public class KnowledgeServiceImpl implements KnowledgeService {
|
||||
|
||||
@Resource
|
||||
private KnowledgeMapper knowledgeMapper;
|
||||
|
||||
/**
|
||||
* 获取窗口列表
|
||||
*
|
||||
* @return 集合
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult getList() {
|
||||
Long userId = SecurityUtils.getUserId();
|
||||
try {
|
||||
return AjaxResult.success(knowledgeMapper.getList(userId));
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增窗口
|
||||
*
|
||||
* @param knowledgeChatWindowVo 实体
|
||||
* @return 集合
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult insertChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo) {
|
||||
try {
|
||||
knowledgeChatWindowVo.setWindowName(knowledgeChatWindowVo.getWindowName());
|
||||
knowledgeChatWindowVo.setCustomerId(SecurityUtils.getUserId());
|
||||
knowledgeChatWindowVo.setCreateTime(new Date());
|
||||
knowledgeChatWindowVo.setUpdateTime(new Date());
|
||||
int l = knowledgeMapper.insertChatWindow(knowledgeChatWindowVo);
|
||||
if (l > 0) {
|
||||
return AjaxResult.success();
|
||||
} else {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新聊天窗口信息
|
||||
*
|
||||
* @param knowledgeChatWindowVo 聊天窗口信息
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult updateChatWindow(KnowledgeChatWindowVo knowledgeChatWindowVo) {
|
||||
try {
|
||||
int i = knowledgeMapper.updateChatWindow(knowledgeChatWindowVo);
|
||||
if (i > 0) {
|
||||
return AjaxResult.success();
|
||||
} else {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑删除聊天窗口
|
||||
*
|
||||
* @param windowId 窗口ID
|
||||
* @return 受影响的行数
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult deleteChatWindow(Long windowId) {
|
||||
try {
|
||||
int i = knowledgeMapper.deleteChatWindow(windowId);
|
||||
if (i > 0) {
|
||||
return AjaxResult.success();
|
||||
} else {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据窗口id获取问答记录
|
||||
*
|
||||
* @param windowId 窗口id
|
||||
* @return 所有问答记录的列表
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult getAllByWindowId(Long windowId) {
|
||||
try {
|
||||
List<AiQuestionAnswer> allByWindowId = knowledgeMapper.getAllByWindowId(windowId);
|
||||
return AjaxResult.success(allByWindowId);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入问答信息
|
||||
*
|
||||
* @param aiQuestionAnswer 问答信息
|
||||
* @return 生成的窗口ID
|
||||
*/
|
||||
@Override
|
||||
public AjaxResult insertQuestionAnswer(AiQuestionAnswer aiQuestionAnswer) {
|
||||
try {
|
||||
Long l = knowledgeMapper.insertQuestionAnswer(aiQuestionAnswer);
|
||||
if (ObjectUtils.isEmpty(l)) {
|
||||
return AjaxResult.error();
|
||||
} else {
|
||||
return AjaxResult.success(l);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
package com.bonus.ai.utils;
|
||||
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.bonus.common.core.utils.http.HttpRequestHelper;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 人脸识别工具类
|
||||
*
|
||||
* @author bonus
|
||||
*/
|
||||
public class FaceUtils {
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
private static final String IP = "192.168.0.14";
|
||||
/**
|
||||
* 端口
|
||||
*/
|
||||
private static final String PORT = "18017";
|
||||
|
||||
// 接口路径常量
|
||||
private static final String UPDATE_DB = "/updatedb";
|
||||
private static final String REFRESH_DB = "/refreshdb";
|
||||
private static final String FACE_RECOGNITION = "/facerecognition";
|
||||
private static final String FEATURE_DETECT = "/featuredetect";
|
||||
|
||||
/**
|
||||
* 获取完整的接口 URL
|
||||
*
|
||||
* @return 完整的 URL
|
||||
*/
|
||||
private static String getFullUrl() {
|
||||
return new StringBuilder()
|
||||
.append("http://")
|
||||
.append(IP)
|
||||
.append(":")
|
||||
.append(PORT)
|
||||
.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param img 图片的本地路径 or base64编码 or url
|
||||
* @param optMode ”add”:添加人脸图片;”delete”:删除人脸图片;”replace”:替换已有人脸图片;
|
||||
* @param uniqueKey 指定要操作的这个人脸的名字
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String updateDb(String img, String optMode, String uniqueKey) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("img", img);
|
||||
params.put("optMode", optMode);
|
||||
params.put("uniqueKey", uniqueKey);
|
||||
String json = JSON.toJSONString(params);
|
||||
return HttpRequestHelper.postJson(getFullUrl(), UPDATE_DB, json, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新人脸库
|
||||
*
|
||||
* @return 返回请求体
|
||||
*/
|
||||
|
||||
public static String refreshDb() {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
return HttpRequestHelper.postJson(getFullUrl(), REFRESH_DB, "", headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对输入人脸进行识别
|
||||
*
|
||||
* @param img 图片的本地路径 or base64编码 or url
|
||||
* @return 人脸信息
|
||||
*/
|
||||
public static String faceRecognition(String img) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("img", img);
|
||||
String json = JSON.toJSONString(params);
|
||||
return HttpRequestHelper.postJson(getFullUrl(), FACE_RECOGNITION, json, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对输入人脸进行特征提取
|
||||
*
|
||||
* @param img 图片的本地路径 or base64编码 or url
|
||||
* @return 人脸信息
|
||||
*/
|
||||
public static String featureDetect(String img) {
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
params.put("img", img);
|
||||
String json = JSON.toJSONString(params);
|
||||
return HttpRequestHelper.postJson(getFullUrl(), FEATURE_DETECT, json, headers);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<?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.bonus.ai.mapper.FaceMapper">
|
||||
|
||||
<!-- 插入人脸信息 -->
|
||||
<insert id="insertFace">
|
||||
INSERT INTO ai_face_databse (name, sex, phone, idcard_number,
|
||||
face_address, create_by, create_time,
|
||||
update_by, update_time)
|
||||
VALUES ( #{name}, #{sex}, #{phone}, #{idCardNumber},
|
||||
#{faceAddress}, #{createBy}, #{createTime},
|
||||
#{updateBy}, #{updateTime})
|
||||
</insert>
|
||||
<insert id="insertFaceResult">
|
||||
insert into ai_facerecognize_result (face_id, service_id, result_it, face_address, recognize_time,
|
||||
response_long, invoke_ip, update_by, update_time, result_type,
|
||||
result_status, failure_reason)
|
||||
values (#{faceId}, #{serviceId}, #{resultIt}, #{faceAddress}, #{recognizeTime}, #{responseLong}, #{invokeIp},
|
||||
#{updateBy}, #{updateTime}, #{resultType}, #{resultStatus}, #{failureReason});
|
||||
|
||||
</insert>
|
||||
|
||||
<!-- 查询全部人脸信息 -->
|
||||
<select id="getList" resultType="com.bonus.ai.domain.vo.FaceVo">
|
||||
select face_id AS faceId,
|
||||
name AS name,
|
||||
sex AS sex,
|
||||
phone AS phone,
|
||||
idcard_number AS idCardNumber,
|
||||
face_address AS faceAddress,
|
||||
create_by AS createBy,
|
||||
create_time AS createTime,
|
||||
update_by AS updateBy,
|
||||
update_time AS updateTime
|
||||
from ai_face_databse
|
||||
where del_flag = '0'
|
||||
</select>
|
||||
|
||||
<!-- 根据 faceId 判断人脸是否存在 -->
|
||||
<select id="existsByFaceId" resultType="java.lang.Integer">
|
||||
SELECT COUNT(1)
|
||||
FROM ai_face_databse
|
||||
WHERE face_id = #{faceId}
|
||||
AND del_flag = '0'
|
||||
</select>
|
||||
<select id="getFaceByIdCardNumber" resultType="com.bonus.ai.domain.vo.FaceVo">
|
||||
select face_id AS faceId,
|
||||
name AS name,
|
||||
sex AS sex,
|
||||
phone AS phone,
|
||||
idcard_number AS idCardNumber,
|
||||
face_address AS faceAddress,
|
||||
create_by AS createBy,
|
||||
create_time AS createTime,
|
||||
update_by AS updateBy,
|
||||
update_time AS updateTime
|
||||
from ai_face_databse
|
||||
where del_flag = '0'
|
||||
and idcard_number = #{idCardNumber}
|
||||
</select>
|
||||
|
||||
<select id="resultStatistics" resultType="com.bonus.ai.domain.vo.FaceResultVo">
|
||||
select result_id AS resultId,
|
||||
face_id AS faceId,
|
||||
service_id AS serviceId,
|
||||
result_it AS resultIt,
|
||||
face_address AS faceAddress,
|
||||
recognize_time AS recognizeTime,
|
||||
response_long AS responseLong,
|
||||
invoke_ip AS invokeIp,
|
||||
update_by AS updateBy,
|
||||
update_time AS updateTime,
|
||||
result_type AS resultType,
|
||||
failure_reason AS failureReason
|
||||
from ai_facerecognize_result
|
||||
where del_flag = '0'
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
<?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.bonus.ai.mapper.KnowledgeMapper">
|
||||
<insert id="insertChatWindow">
|
||||
insert into ai_chat_window (knowledge_id, window_name, chat_type, customer_id, create_time,
|
||||
update_time, remark)
|
||||
values (#{knowledgeId}, #{windowName}, #{chatType}, #{customerId}, now(), now(), #{remark});
|
||||
</insert>
|
||||
<insert id="insertQuestionAnswer" useGeneratedKeys="true" keyProperty="recordId">
|
||||
INSERT INTO ai_question_answer
|
||||
(window_id, question, answer, knowledge, del_flag, customer_id, update_time, remark)
|
||||
VALUES (#{windowId}, #{question}, #{answer}, #{knowledge}, #{delFlag}, #{customerId}, #{updateTime}, #{remark});
|
||||
</insert>
|
||||
|
||||
<select id="getList" resultType="com.bonus.ai.domain.KnowledgeChatWindowVo">
|
||||
select window_id AS windowId,
|
||||
knowledge_id AS knowledgeId,
|
||||
window_name AS windowName,
|
||||
chat_type AS chatType,
|
||||
customer_id AS customerId,
|
||||
create_time AS createTime,
|
||||
update_time AS updateTime,
|
||||
remark AS remark
|
||||
from ai_chat_window
|
||||
where del_flag = '0'
|
||||
AND customer_id = #{userId}
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
<select id="getAllByWindowId" resultType="com.bonus.ai.domain.vo.AiQuestionAnswer">
|
||||
SELECT record_id AS recordId,
|
||||
window_id AS windowId,
|
||||
question AS question,
|
||||
answer AS answer,
|
||||
knowledge AS knowledge,
|
||||
customer_id AS customerId,
|
||||
update_time AS updateTime,
|
||||
remark AS remark
|
||||
FROM ai_question_answer
|
||||
where window_id = #{windowId}
|
||||
AND del_flag = '0'
|
||||
</select>
|
||||
|
||||
<update id="updateChatWindow">
|
||||
update ai_chat_window
|
||||
set window_name = #{windowName},
|
||||
chat_type = #{chatType},
|
||||
update_time = now(),
|
||||
remark = #{remark}
|
||||
where window_id = #{windowId};
|
||||
</update>
|
||||
|
||||
<update id="deleteChatWindow">
|
||||
update ai_chat_window
|
||||
set del_flag = '1',
|
||||
update_time = now()
|
||||
where window_id = #{windowId};
|
||||
</update>
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue