Compare commits

...

5 Commits

  1. 189
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/FileController.java
  2. 49
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/KnowLedgeController.java
  3. 4
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/LlmKnowledgeController/QAController.java
  4. 4
      ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/AItokenService.java
  5. 2
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquBaseMapper.xml

@ -0,0 +1,189 @@
package com.alops.web.controller.LlmKnowledgeController;
import java.io.File;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alops.chat.entity.UploadedFile;
import com.alops.chat.service.UploadedFileService;
import com.alops.common.config.RuoYiConfig;
import com.alops.common.core.controller.BaseController;
import com.alops.common.utils.file.FileUtils;
import com.alops.common.utils.file.FileTypeUtils;
import com.alops.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* 文件下载和预览控制器
*
* @author lingma
*/
@RestController
@RequestMapping("/QandA/knowledge/file")
public class FileController extends BaseController {
private static final Logger log = LoggerFactory.getLogger(FileController.class);
@Autowired
private UploadedFileService uploadedFileService;
/**
* 下载文件
*
* @param fileId 文件ID
* @param response 响应对象
*/
@PreAuthorize("@ss.hasPermi('QandA:knowledge:filedownload')")
@GetMapping("/download/{fileId}")
public void download(@PathVariable Long fileId, HttpServletResponse response, HttpServletRequest request) {
try {
UploadedFile uploadedFile = uploadedFileService.findById(fileId);
if (uploadedFile == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到");
return;
}
// 获取文件路径
String filePath = uploadedFile.getFilePath();
// 获取实际文件路径(处理配置文件前缀)
String realPath = RuoYiConfig.getProfile() + filePath.replaceFirst("/profile", "");
// 检查文件是否存在
File file = new File(realPath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到");
return;
}
// 检查文件是否允许下载
if (!FileUtils.checkAllowDownload(realPath)) {
throw new Exception(StringUtils.format("文件({})非法,不允许下载。", realPath));
}
// 设置下载文件名(从文件路径中提取真实文件名)
String fileName = FileUtils.getName(realPath);
if (StringUtils.isEmpty(fileName)) {
fileName = uploadedFile.getSource();
// 如果source中也不包含扩展名,则尝试添加扩展名
String extension = uploadedFile.getFormat();
if (StringUtils.isNotEmpty(extension) && !fileName.endsWith("." + extension)) {
fileName = fileName + "." + extension;
}
}
// 对文件名进行编码以支持中文
String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString())
.replaceAll("\\+", "%20");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
FileUtils.writeBytes(realPath, response.getOutputStream());
} catch (Exception e) {
log.error("下载文件失败", e);
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "下载文件失败: " + e.getMessage());
} catch (IOException ioException) {
log.error("设置错误响应失败", ioException);
}
}
}
/**
* 预览文件
*
* @param fileId 文件ID
* @param response 响应对象
*/
@PreAuthorize("@ss.hasPermi('QandA:knowledge:filepreview')")
@GetMapping("/preview/{fileId}")
public void preview(@PathVariable Long fileId, HttpServletResponse response, HttpServletRequest request) {
try {
UploadedFile uploadedFile = uploadedFileService.findById(fileId);
if (uploadedFile == null) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到");
return;
}
// 获取文件路径
String filePath = uploadedFile.getFilePath();
// 获取实际文件路径(处理配置文件前缀)
String realPath = RuoYiConfig.getProfile() + filePath.replaceFirst("/profile", "");
// 检查文件是否存在
File file = new File(realPath);
if (!file.exists()) {
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件未找到");
return;
}
// 检查文件是否允许预览
if (!FileUtils.checkAllowDownload(realPath)) {
throw new Exception(StringUtils.format("文件({})非法,不允许预览。", realPath));
}
// 设置Content-Type以便浏览器预览
String fileExtension = FileTypeUtils.getFileType(uploadedFile.getSource());
if (StringUtils.isEmpty(fileExtension)) {
fileExtension = uploadedFile.getFormat();
}
String contentType = getContentTypeByExtension(fileExtension);
if (contentType != null) {
response.setContentType(contentType);
} else {
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
}
FileUtils.writeBytes(realPath, response.getOutputStream());
} catch (Exception e) {
log.error("预览文件失败", e);
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "预览文件失败: " + e.getMessage());
} catch (IOException ioException) {
log.error("设置错误响应失败", ioException);
}
}
}
/**
* 根据文件扩展名获取对应的Content-Type
*
* @param extension 文件扩展名
* @return Content-Type字符串
*/
private String getContentTypeByExtension(String extension) {
if (StringUtils.isEmpty(extension)) {
return MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
switch (extension.toLowerCase()) {
case "txt":
return "text/plain;charset=UTF-8";
case "doc":
return "application/msword";
case "docx":
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
case "pdf":
return "application/pdf";
case "jpg":
case "jpeg":
return "image/jpeg";
case "png":
return "image/png";
case "gif":
return "image/gif";
default:
return MediaType.APPLICATION_OCTET_STREAM_VALUE;
}
}
}

@ -24,14 +24,9 @@ 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.HashMap;
import java.util.List;
import java.util.Map;
/**
* 文件上传控制器
@ -51,6 +46,36 @@ public class KnowLedgeController extends BaseController {
// 文件存储目录
private static final String UPLOAD_DIR = "uploads/";
/**
* 根据文件ID获取文件路径供前端下载和预览使用
*
* @param id 文件ID
* @return 文件路径信息
*/
@PreAuthorize("@ss.hasPermi('QandA:knowledge:filedownload')")
@GetMapping("/file-path/{id}")
@ApiOperation("获取文件路径")
public AjaxResult getFilePath(@PathVariable Long id) {
try {
UploadedFile uploadedFile = uploadedFileService.findById(id);
if (uploadedFile == null) {
return AjaxResult.error(HttpStatus.NOT_FOUND, "未找到指定的文件");
}
// 返回文件路径信息
Map<String, Object> result = new HashMap<>();
result.put("filePath", uploadedFile.getFilePath());
result.put("fileName", uploadedFile.getSource());
result.put("fileFormat", uploadedFile.getFormat());
result.put("uploadTime", uploadedFile.getUploadTime());
return AjaxResult.success(result);
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("获取文件路径失败: " + e.getMessage());
}
}
/**
* 上传并处理文本文档
*
@ -81,14 +106,14 @@ public class KnowLedgeController extends BaseController {
// 使用优化后的文件上传工具保存文件到本地并记录信息到数据库
// 上传文件并保持原始文件名,只允许 TEXT_EXTENSION 类型的文件
String filePath = FileUploadUtils.uploadWithOriginalFilename(
RuoYiConfig.getProfile(),
file,
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();
@ -273,7 +298,7 @@ public class KnowLedgeController extends BaseController {
try {
// 先删除关联的知识条目
knowledgeService.deleteKnowledgeByFileId(id);
// 再删除文件信息
uploadedFileService.deleteById(id);
return AjaxResult.success("上传文件信息删除成功");

@ -87,12 +87,12 @@ public class QAController {
List<Map<String, String>> messages = new ArrayList<>();
Map<String, String> systemMessage = new HashMap<>();
systemMessage.put("role", "system");
systemMessage.put("content", "你是一个智能运维助手,根据提供的上下文回答问题。如果上下文中没有相关信息,请说明无法基于提供的上下文回答问题。");
systemMessage.put("content", "你是一个智能运维助手,根据提供的上下文回答问题。如果上下文中没有相关信息,请结合自身知识回答用户问题。");
messages.add(systemMessage);
Map<String, String> userMessage = new HashMap<>();
userMessage.put("role", "user");
userMessage.put("content", "上下文信息:\n" + context + "\n\n请根据以上上下文信息回答问题:" + request.getQuestion());
userMessage.put("content", "上下文信息:\n" + context + "\n\n请根据以上上下文信息并且结合自身知识回答问题:" + request.getQuestion());
messages.add(userMessage);
String answer = AItokenService.generateAnswer(messages);

@ -30,7 +30,7 @@ public class AItokenService {
// 使用的模型
private static final String EMBEDDING_MODEL = "nomic-embed-text:v1.5";
private static final String GENERATION_MODEL = "qwen3:4b";
private static final String GENERATION_MODEL = "qwen3:8b";
// 是否使用假数据
@Value("${mock.enabled:false}")
@ -262,7 +262,7 @@ public class AItokenService {
requestBody.put("model", GENERATION_MODEL);
requestBody.put("messages", messages);
requestBody.put("stream", false);
requestBody.put("options", Map.of("num_predict", 2048)); // 限制生成长度以减少资源消耗
requestBody.put("options", Map.of("num_predict", 8192)); // 限制生成长度以减少资源消耗
// 设置请求头
HttpHeaders headers = new HttpHeaders();

@ -278,7 +278,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
<!-- 精确查询:状态 -->
<if test="status != null and status != ''">
<if test="status != null ">
AND b.status = #{status}
</if>
</where>

Loading…
Cancel
Save