diff --git a/bonus-modules/bonus-ai/pom.xml b/bonus-modules/bonus-ai/pom.xml
index fc0f0c4..3adb592 100644
--- a/bonus-modules/bonus-ai/pom.xml
+++ b/bonus-modules/bonus-ai/pom.xml
@@ -5,7 +5,7 @@
com.bonus
bonus-modules
- 24.7.1-SNAPSHOT
+ 24.7.1
4.0.0
@@ -55,6 +55,11 @@
bonus-common-datasource
+
+ com.bonus
+ bonus-common-core
+
+
com.bonus
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/FaceController.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/FaceController.java
new file mode 100644
index 0000000..4b82324
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/FaceController.java
@@ -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();
+ }
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/KnowledgeController.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/KnowledgeController.java
new file mode 100644
index 0000000..fb5335d
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/controller/KnowledgeController.java
@@ -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);
+ }
+
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/KnowledgeChatWindowVo.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/KnowledgeChatWindowVo.java
new file mode 100644
index 0000000..341fc59
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/KnowledgeChatWindowVo.java
@@ -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;
+
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/AiQuestionAnswer.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/AiQuestionAnswer.java
new file mode 100644
index 0000000..a366c1c
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/AiQuestionAnswer.java
@@ -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;
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/FaceResultVo.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/FaceResultVo.java
new file mode 100644
index 0000000..d36c1d5
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/FaceResultVo.java
@@ -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;
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/FaceVo.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/FaceVo.java
new file mode 100644
index 0000000..ee4bb4c
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/domain/vo/FaceVo.java
@@ -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;
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/FaceMapper.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/FaceMapper.java
new file mode 100644
index 0000000..787dfdf
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/FaceMapper.java
@@ -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 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 getFaceByIdCardNumber(String idCardNumber);
+
+ /**
+ * 插入人脸识别记录
+ *
+ * @param faceResultVo 人脸信息对象
+ * @return 受影响的行数
+ */
+ int insertFaceResult(FaceResultVo faceResultVo);
+
+ /**
+ * 识别结果统计
+ *
+ * @return 结果
+ */
+ List resultStatistics();
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/KnowledgeMapper.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/KnowledgeMapper.java
new file mode 100644
index 0000000..edc0a29
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/mapper/KnowledgeMapper.java
@@ -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 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 getAllByWindowId(Long windowId);
+
+ /**
+ * 插入问答信息
+ *
+ * @param aiQuestionAnswer 问答信息
+ * @return 生成的窗口ID
+ */
+ Long insertQuestionAnswer(AiQuestionAnswer aiQuestionAnswer);
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/FaceService.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/FaceService.java
new file mode 100644
index 0000000..17bf5bb
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/FaceService.java
@@ -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();
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/KnowledgeService.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/KnowledgeService.java
new file mode 100644
index 0000000..59b1798
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/KnowledgeService.java
@@ -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);
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/impl/FaceServiceImpl.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/impl/FaceServiceImpl.java
new file mode 100644
index 0000000..13e1512
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/impl/FaceServiceImpl.java
@@ -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 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 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();
+ }
+ }
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/impl/KnowledgeServiceImpl.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/impl/KnowledgeServiceImpl.java
new file mode 100644
index 0000000..d0ebf0d
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/service/impl/KnowledgeServiceImpl.java
@@ -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 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();
+ }
+ }
+}
diff --git a/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/FaceUtils.java b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/FaceUtils.java
new file mode 100644
index 0000000..1b5851d
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/java/com/bonus/ai/utils/FaceUtils.java
@@ -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 headers = new HashMap<>();
+ Map params = new HashMap();
+ 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 headers = new HashMap<>();
+ return HttpRequestHelper.postJson(getFullUrl(), REFRESH_DB, "", headers);
+ }
+
+ /**
+ * 对输入人脸进行识别
+ *
+ * @param img 图片的本地路径 or base64编码 or url
+ * @return 人脸信息
+ */
+ public static String faceRecognition(String img) {
+ Map headers = new HashMap<>();
+ Map params = new HashMap();
+ 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 params = new HashMap();
+ Map headers = new HashMap<>();
+ params.put("img", img);
+ String json = JSON.toJSONString(params);
+ return HttpRequestHelper.postJson(getFullUrl(), FEATURE_DETECT, json, headers);
+ }
+
+
+}
diff --git a/bonus-modules/bonus-ai/src/main/resources/mapper/ai/FaceMapper.xml b/bonus-modules/bonus-ai/src/main/resources/mapper/ai/FaceMapper.xml
new file mode 100644
index 0000000..f0a0006
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/resources/mapper/ai/FaceMapper.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+ 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 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});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bonus-modules/bonus-ai/src/main/resources/mapper/ai/KnowledgeMapper.xml b/bonus-modules/bonus-ai/src/main/resources/mapper/ai/KnowledgeMapper.xml
new file mode 100644
index 0000000..aee8384
--- /dev/null
+++ b/bonus-modules/bonus-ai/src/main/resources/mapper/ai/KnowledgeMapper.xml
@@ -0,0 +1,60 @@
+
+
+
+
+ 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 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});
+
+
+
+
+
+
+ update ai_chat_window
+ set window_name = #{windowName},
+ chat_type = #{chatType},
+ update_time = now(),
+ remark = #{remark}
+ where window_id = #{windowId};
+
+
+
+ update ai_chat_window
+ set del_flag = '1',
+ update_time = now()
+ where window_id = #{windowId};
+
+