From 342fe3af5a2868c37ed67d95ee9d4f06c1667772 Mon Sep 17 00:00:00 2001
From: 0214 <2028008386@qq.com>
Date: Wed, 24 Sep 2025 22:06:06 +0800
Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=B0=E7=9A=84alops=5Fsys?=
=?UTF-8?q?.sql=E6=96=87=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ALOps_sys_backend/alops-admin/pom.xml | 17 +-
.../main/java/com/alops/AlopsApplication.java | 1 +
.../KnowLedgeController.java | 286 ++
.../LlmKnowledgeController/QAController.java | 139 +
.../src/main/resources/application.yml | 21 +-
ALOps_sys_backend/alops-chat/pom.xml | 152 +
.../com/alops/chat/DTO/AnswerResponse.java | 34 +
.../com/alops/chat/DTO/EmbeddingResponse.java | 61 +
.../com/alops/chat/DTO/QuestionRequest.java | 20 +
.../alops/chat/entity/KnowledgeEntity.java | 94 +
.../com/alops/chat/entity/UploadedFile.java | 60 +
.../chat/repository/KnowledgeRepository.java | 55 +
.../alops/chat/service/AItokenService.java | 750 +++++
.../alops/chat/service/EmbeddingService.java | 113 +
.../alops/chat/service/KnowledgeService.java | 469 +++
.../com/alops/chat/service/TextSplitter.java | 298 ++
.../chat/service/UploadedFileService.java | 52 +
.../common/utils/file/FileUploadUtils.java | 32 +-
.../common/utils/file/MimeTypeUtils.java | 2 +
.../framework/config/ApplicationConfig.java | 11 +
ALOps_sys_backend/alops-system/pom.xml | 9 +
.../java/com/alops/system/job/GatherJob.java | 4 +-
ALOps_sys_sql/aiops_sys.sql | 2534 +++++++++++++++++
23 files changed, 5202 insertions(+), 12 deletions(-)
create mode 100644 ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/KnowLedgeController.java
create mode 100644 ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/QAController.java
create mode 100644 ALOps_sys_backend/alops-chat/pom.xml
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/DTO/AnswerResponse.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/DTO/EmbeddingResponse.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/DTO/QuestionRequest.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/entity/KnowledgeEntity.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/entity/UploadedFile.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/repository/KnowledgeRepository.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/AItokenService.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/EmbeddingService.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/KnowledgeService.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/TextSplitter.java
create mode 100644 ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/UploadedFileService.java
create mode 100644 ALOps_sys_sql/aiops_sys.sql
diff --git a/ALOps_sys_backend/alops-admin/pom.xml b/ALOps_sys_backend/alops-admin/pom.xml
index c5e46b6a..5adccf07 100644
--- a/ALOps_sys_backend/alops-admin/pom.xml
+++ b/ALOps_sys_backend/alops-admin/pom.xml
@@ -11,6 +11,8 @@
4.0.0
jar
alops-admin
+
+
web服务入口
@@ -45,10 +47,11 @@
1.6.2
-
+
- mysql
- mysql-connector-java
+ com.mysql
+ mysql-connector-j
+ 8.0.33
@@ -76,12 +79,20 @@
alops-generator
+
org.springframework.boot
spring-boot-starter-mail
2.5.15
+
+ com.alops
+ alops-chat
+ 1.0.0
+ compile
+
+
diff --git a/ALOps_sys_backend/alops-admin/src/main/java/com/alops/AlopsApplication.java b/ALOps_sys_backend/alops-admin/src/main/java/com/alops/AlopsApplication.java
index f07acc88..f09f2d36 100644
--- a/ALOps_sys_backend/alops-admin/src/main/java/com/alops/AlopsApplication.java
+++ b/ALOps_sys_backend/alops-admin/src/main/java/com/alops/AlopsApplication.java
@@ -11,6 +11,7 @@ import org.springframework.context.annotation.ComponentScan;
* @author ruoyi
*/
@SpringBootApplication (exclude = { DataSourceAutoConfiguration.class })
+@ComponentScan(basePackages = {"com.alops.web", "com.alops.framework", "com.alops.system", "com.alops.quartz", "com.alops.common", "com.alops.generator", "com.alops.chat"})
public class AlopsApplication
diff --git a/ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/KnowLedgeController.java b/ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/KnowLedgeController.java
new file mode 100644
index 00000000..7695197d
--- /dev/null
+++ b/ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/KnowLedgeController.java
@@ -0,0 +1,286 @@
+package com.alops.web.controller.LlmKnowledgeController;
+
+import com.alops.chat.entity.KnowledgeEntity;
+import com.alops.chat.entity.UploadedFile;
+import com.alops.chat.service.KnowledgeService;
+import com.alops.chat.service.UploadedFileService;
+import com.alops.common.constant.HttpStatus;
+import com.alops.common.core.controller.BaseController;
+import com.alops.common.core.domain.AjaxResult;
+import com.alops.common.core.page.TableDataInfo;
+import com.alops.common.config.RuoYiConfig;
+import com.alops.common.utils.file.FileUploadUtils;
+import com.alops.common.utils.file.MimeTypeUtils;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 文件上传控制器
+ * 提供文件导入功能,将文本文档转化为知识条目
+ */
+@RestController
+@RequestMapping("/QandA/knowledge/")
+@CrossOrigin(origins = "*")
+public class KnowLedgeController extends BaseController {
+
+ @Autowired
+ private KnowledgeService knowledgeService;
+
+ @Autowired
+ private UploadedFileService uploadedFileService;
+
+ // 文件存储目录
+ private static final String UPLOAD_DIR = "uploads/";
+
+ /**
+ * 上传并处理文本文档
+ *
+ * @param file 上传的文件
+ * @param source 文件来源描述
+ * @return 处理结果
+ */
+
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:uploadfile')")
+ @PostMapping("/upload")
+ @ApiOperation("上传并处理文本文档")
+ public AjaxResult uploadFile(
+ @RequestPart("file")
+ @ApiParam(value = "知识库文件", type = "file") MultipartFile file,
+ @ModelAttribute("source") String source) {
+ try {
+ String content;
+
+ // 判断文件类型并读取内容
+ if (file.getOriginalFilename() != null && file.getOriginalFilename().endsWith(".docx")) {
+ // 处理docx文件
+ content = readDocxFile(file);
+ } else {
+ // 处理txt文件
+ content = readTxtFile(file);
+ }
+
+ // 使用优化后的文件上传工具保存文件到本地并记录信息到数据库
+ // 上传文件并保持原始文件名,只允许 TEXT_EXTENSION 类型的文件
+ String filePath = FileUploadUtils.uploadWithOriginalFilename(
+ RuoYiConfig.getProfile(),
+ file,
+ MimeTypeUtils.TEXT_EXTENSION);
+
+ String fileExtension = getFileExtension(file.getOriginalFilename());
+ UploadedFile uploadedFile = new UploadedFile(filePath, source, fileExtension, null);
+ uploadedFile = uploadedFileService.save(uploadedFile);
+
+ // 获取保存后的文件ID
+ Long fileId = uploadedFile.getId();
+
+ // 处理文件内容并添加到知识库,将文件ID作为keyfileid参数传递
+ List knowledgeIds = knowledgeService.addKnowledgeWithFileId(content, source, fileId);
+
+ // 更新上传文件信息(如果有知识条目被创建)
+ if (!knowledgeIds.isEmpty()) {
+ // 可以在这里添加其他需要的更新逻辑
+ }
+
+ return AjaxResult.success("文件上传并处理成功");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error("文件处理失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 读取docx文件的内容
+ *
+ * @param file 上传的docx文件
+ * @return 文件内容字符串
+ * @throws IOException IO异常
+ */
+ private String readDocxFile(MultipartFile file) throws IOException {
+ try (XWPFDocument document = new XWPFDocument(file.getInputStream())) {
+ StringBuilder content = new StringBuilder();
+ // 遍历所有段落并提取文本
+ for (XWPFParagraph paragraph : document.getParagraphs()) {
+ content.append(paragraph.getText()).append("\n");
+ }
+ return content.toString();
+ }
+ }
+
+ /**
+ * 读取txt文件的内容
+ *
+ * @param file 上传的txt文件
+ * @return 文件内容字符串
+ * @throws IOException IO异常
+ */
+ private String readTxtFile(MultipartFile file) throws IOException {
+ StringBuilder content = new StringBuilder();
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ content.append(line).append("\n");
+ }
+ }
+ return content.toString();
+ }
+
+
+
+ /**
+ * 获取文件扩展名
+ *
+ * @param filename 文件名
+ * @return 文件扩展名
+ */
+ private String getFileExtension(String filename) {
+ if (filename == null || filename.lastIndexOf(".") == -1) {
+ return "";
+ }
+ return filename.substring(filename.lastIndexOf(".") + 1);
+ }
+
+ /**
+ * 添加知识条目接口
+ *
+ * @param content 知识内容
+ * @param source 知识来源
+ * @return 添加结果
+ */
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:upload')")
+ @PostMapping("/add-knowledge")
+ @ApiOperation("添加知识条目接口")
+ public AjaxResult addKnowledge(
+ @RequestParam String content,
+ @RequestParam String source) {
+ try {
+ knowledgeService.addKnowledge(content, source);
+ return AjaxResult.success("知识添加成功");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error("知识添加失败: " + e.getMessage());
+ }
+ }
+
+
+ /**
+ * 获取所有正式知识条目(分页)
+ *
+ * @return 正式知识条目列表
+ */
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:permquery')")
+ @GetMapping("/permanent-knowledge")
+ @ApiOperation("获取所有正式知识条目")
+ public TableDataInfo getPermanentKnowledge() {
+ startPage(); // 开启分页
+ List permanentEntries = knowledgeService.getPermanentEntries();
+ return getDataTable(permanentEntries);
+ }
+
+ /**
+ * 获取所有上传文件信息(分页)
+ *
+ * @return 上传文件信息列表
+ */
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:filelist')")
+ @GetMapping("/uploaded-files")
+ @ApiOperation("获取所有上传文档")
+ public TableDataInfo getUploadedFiles() {
+ startPage(); // 开启分页
+ List uploadedFiles = uploadedFileService.findAll();
+ return getDataTable(uploadedFiles);
+ }
+
+ /**
+ * 更新知识条目接口
+ *
+ * @param id 知识条目ID
+ * @param content 知识内容
+ * @param source 知识来源
+ * @return 更新结果
+ */
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:update')")
+ @PutMapping("/update-knowledge")
+ @ApiOperation("更新知识条目")
+ public AjaxResult updateKnowledge(
+ @RequestParam Long id,
+ @RequestParam String content,
+ @RequestParam String source) {
+ try {
+ boolean updated = knowledgeService.updateKnowledge(id, content, source);
+ if (updated) {
+ return AjaxResult.success("知识条目更新成功");
+ } else {
+ return AjaxResult.error(HttpStatus.NOT_FOUND, "未找到指定的知识条目");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error("知识条目更新失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 删除知识条目接口
+ *
+ * @param id 知识条目ID
+ * @return 删除结果
+ */
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:del')")
+ @DeleteMapping("/knowledge/{id}")
+ @ApiOperation("删除知识条目接口")
+ public AjaxResult deleteKnowledge(@PathVariable Long id) {
+ try {
+ boolean deleted = knowledgeService.deleteKnowledge(id);
+ if (deleted) {
+ return AjaxResult.success("知识条目删除成功");} else {
+ return AjaxResult.error(HttpStatus.NOT_FOUND, "未找到指定的知识条目");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error("知识条目删除失败: " + e.getMessage());
+ }
+ }
+
+ /**
+ * 删除上传文件信息接口
+ *
+ * @param id 上传文件ID
+ * @return 删除结果
+ */
+ @ApiOperation("删除上传文件信息")
+ @PreAuthorize("@ss.hasPermi('QandA:knowledge:filedel')")
+ @DeleteMapping("/uploaded-files/{id}")
+ public AjaxResult deleteUploadedFile(@PathVariable Long id) {
+ try {
+ // 先删除关联的知识条目
+ knowledgeService.deleteKnowledgeByFileId(id);
+
+ // 再删除文件信息
+ uploadedFileService.deleteById(id);
+ return AjaxResult.success("上传文件信息删除成功");
+ } catch (Exception e) {
+ e.printStackTrace();
+ return AjaxResult.error("上传文件信息删除失败: " + e.getMessage());
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/QAController.java b/ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/QAController.java
new file mode 100644
index 00000000..6283883a
--- /dev/null
+++ b/ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/QAController.java
@@ -0,0 +1,139 @@
+package com.alops.web.controller.LlmKnowledgeController;
+
+import com.alops.chat.DTO.AnswerResponse;
+import com.alops.chat.DTO.QuestionRequest;
+import com.alops.chat.entity.KnowledgeEntity;
+import com.alops.chat.service.AItokenService;
+import com.alops.chat.service.KnowledgeService;
+import com.alops.common.core.domain.AjaxResult;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 问答系统控制器
+ * 提供基于知识库的问答接口
+ */
+
+@RestController
+@RequestMapping("/QandA/knowledge/")
+@CrossOrigin(origins = "*")
+public class QAController {
+
+ @Autowired
+ private KnowledgeService knowledgeService;
+
+ @Autowired
+ private AItokenService AItokenService;
+
+ // 假数据答案
+ @Value("${mock.qa.answer:这是默认的模拟答案}")
+ private String mockAnswer;
+
+ // 假数据来源
+ @Value("${mock.qa.sources:模拟来源1,模拟来源2}")
+ private String mockSources;
+
+ /**
+ * POST方式问答接口
+ * @param request 包含问题的请求对象
+ * @return 答案响应
+ */
+ @PreAuthorize("@ss.hasPermi('QandA:Aiassistant:ask')")
+ @PostMapping("/ask")
+ @ApiOperation("智能问答提问")
+ public AjaxResult askQuestion(@RequestBody QuestionRequest request) {
+ try {
+ // 检查是否使用假数据模式
+ if (AItokenService.isMockEnabled()) {
+ System.out.println("使用假数据模式返回问答结果");
+ String[] sources = mockSources.split(",");
+ AnswerResponse answerResponse = new AnswerResponse(mockAnswer, sources);
+ return AjaxResult.success("问答成功(假数据模式)", answerResponse);
+ }
+
+ // 1. 查找相关知识(最相似的8条)
+ List similarKnowledge = knowledgeService.findSimilarKnowledge(request.getQuestion(), 8);
+
+ // 如果没有找到相关知识,返回提示信息
+ if (similarKnowledge.isEmpty()) {
+ // 创建一个专门的提示信息,说明知识库中未找到相关内容
+ String noAnswerPrompt = "知识库中没有找到与您的问题直接相关的内容。您的问题可能是关于 \"" +
+ request.getQuestion() + "\"。请确认问题表述是否准确,或者尝试重新表述问题。" +
+ "如果这是一个重要的问题,请联系管理员添加相关知识。";
+
+ AnswerResponse answerResponse = new AnswerResponse(noAnswerPrompt, new String[0]);
+ return AjaxResult.success(noAnswerPrompt, answerResponse);
+ }
+
+ // 2. 生成上下文
+ String context = knowledgeService.generateContext(similarKnowledge);
+
+ // 检查上下文是否为空
+ if (context == null || context.trim().isEmpty()) {
+ String errorMessage = "系统未能从知识库中提取有效上下文信息。";
+ AnswerResponse errorResponse = new AnswerResponse(errorMessage, new String[0]);
+ return AjaxResult.error(errorMessage, errorResponse);
+ }
+
+ // 3. 构造消息列表并调用Qwen3生成答案
+ List