diff --git a/HikariCP连接池问题分析.md b/HikariCP连接池问题分析.md index 8e2a922..31a09e7 100644 --- a/HikariCP连接池问题分析.md +++ b/HikariCP连接池问题分析.md @@ -121,3 +121,5 @@ hikari: + + diff --git a/src/main/java/com/rj/controller/KnowledgeBaseController.java b/src/main/java/com/rj/controller/KnowledgeBaseController.java index a9c4c14..0f79705 100644 --- a/src/main/java/com/rj/controller/KnowledgeBaseController.java +++ b/src/main/java/com/rj/controller/KnowledgeBaseController.java @@ -2,13 +2,17 @@ package com.rj.controller; import com.rj.entity.KnowledgeBase; import com.rj.service.IKnowledgeBaseService; +import com.rj.service.MinIOService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,6 +24,7 @@ import java.util.Map; * @author 李中华 ,spllzh * @since 2025-01-27 */ +@Slf4j @RestController @RequestMapping("/api/knowledgeBase") @Tag(name = "知识库管理", description = "知识库管理相关接口") @@ -28,14 +33,35 @@ public class KnowledgeBaseController { @Autowired private IKnowledgeBaseService knowledgeBaseService; + @Autowired + private MinIOService minIOService; + /** * 新增知识 */ @PostMapping("/add") @Operation(summary = "新增知识", description = "添加新的知识信息") public ResponseEntity> addKnowledge( + @RequestParam(value = "file", required = false) MultipartFile file, @Parameter(description = "知识信息", required = true) - @RequestBody KnowledgeBase knowledgeBase) { + @ModelAttribute KnowledgeBase knowledgeBase) { + // 如果提供了文件,上传到MinIO并保存路径 + if (file != null && !file.isEmpty()) { + try { + log.info("开始上传文件到MinIO,文件名:{}", file.getOriginalFilename()); + String fileUrl = minIOService.uploadFile(file); + log.info("文件上传到MinIO成功,URL: {}", fileUrl); + // 将上传的文件URL保存到知识库对象的附件URL字段 + knowledgeBase.setAttachmentUrl(fileUrl); + } catch (Exception e) { + log.error("文件上传到MinIO失败: {}", e.getMessage(), e); + Map errorResult = new HashMap<>(); + errorResult.put("success", false); + errorResult.put("message", "文件上传失败:" + e.getMessage()); + return ResponseEntity.internalServerError().body(errorResult); + } + } + Map result = knowledgeBaseService.addKnowledge(knowledgeBase); Boolean success = (Boolean) result.get("success"); if (success != null && success) { diff --git a/src/main/java/com/rj/service/impl/KnowledgeBaseServiceImpl.java b/src/main/java/com/rj/service/impl/KnowledgeBaseServiceImpl.java index 4f2792b..af6874e 100644 --- a/src/main/java/com/rj/service/impl/KnowledgeBaseServiceImpl.java +++ b/src/main/java/com/rj/service/impl/KnowledgeBaseServiceImpl.java @@ -5,9 +5,16 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.rj.entity.KnowledgeBase; import com.rj.mapper.KnowledgeBaseMapper; import com.rj.service.IKnowledgeBaseService; +import com.rj.service.MinIOService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.util.HashMap; import java.util.List; @@ -22,9 +29,32 @@ import java.util.UUID; * @author 李中华 ,spllzh * @since 2025-01-27 */ +@Slf4j @Service public class KnowledgeBaseServiceImpl extends ServiceImpl implements IKnowledgeBaseService { + @Autowired + private MinIOService minIOService; + + /** + * 文本文件内容预览的最大字符数 + */ + private static final int MAX_FILE_CONTENT_PREVIEW_LENGTH = 500; + + /** + * 默认状态 + */ + private static final String DEFAULT_STATUS = "已发布"; + + /** + * 支持的文本文件扩展名 + */ + private static final String[] TEXT_FILE_EXTENSIONS = { + ".txt", ".md", ".json", ".xml", ".csv", ".log", + ".properties", ".yml", ".yaml", ".html", ".htm", + ".css", ".js", ".java", ".py", ".sql", ".sh", ".bat" + }; + @Override public Map addKnowledge(KnowledgeBase knowledgeBase) { Map result = new HashMap<>(); @@ -47,8 +77,8 @@ public class KnowledgeBaseServiceImpl extends ServiceImpl getKnowledgeList(Integer current, Integer size, String title, String category, String industry, String creator, String status) { - Map result = new HashMap<>(); try { + // 参数验证和默认值设置 + if (current == null || current < 1) { + current = 1; + } + if (size == null || size < 1) { + size = 10; + } + + // 构建查询条件 + LambdaQueryWrapper queryWrapper = buildQueryWrapper(title, category, industry, creator, status); + + // 执行分页查询 Page page = new Page<>(current, size); - LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); - - // 添加查询条件 - if (title != null && !title.trim().isEmpty()) { - queryWrapper.like(KnowledgeBase::getTitle, title); - } - if (category != null && !category.trim().isEmpty()) { - queryWrapper.eq(KnowledgeBase::getCategory, category); - } - if (industry != null && !industry.trim().isEmpty()) { - queryWrapper.eq(KnowledgeBase::getIndustry, industry); - } - if (creator != null && !creator.trim().isEmpty()) { - queryWrapper.like(KnowledgeBase::getCreator, creator); - } - if (status != null && !status.trim().isEmpty()) { - queryWrapper.eq(KnowledgeBase::getStatus, status); - } - - // 排序:置顶优先,然后按排序权重,最后按创建时间倒序 - queryWrapper.orderByDesc(KnowledgeBase::getUpdateTime); - Page knowledgePage = this.page(page, queryWrapper); - - result.put("success", true); - result.put("message", "查询成功"); - result.put("data", knowledgePage.getRecords()); - result.put("total", knowledgePage.getTotal()); - result.put("current", knowledgePage.getCurrent()); - result.put("size", knowledgePage.getSize()); - result.put("pages", knowledgePage.getPages()); + + // 处理附件文件内容读取(可选,用于调试) + processAttachmentFiles(knowledgePage.getRecords()); + + // 构建返回结果 + return buildSuccessResult(knowledgePage); + } catch (Exception e) { - result.put("success", false); - result.put("message", "查询异常:" + e.getMessage()); + log.error("查询知识列表异常", e); + return buildErrorResult("查询异常:" + e.getMessage()); } + } + + /** + * 构建查询条件 + * + * @param title 标题(模糊查询) + * @param category 分类 + * @param industry 行业 + * @param creator 创建人(模糊查询) + * @param status 状态 + * @return 查询条件包装器 + */ + private LambdaQueryWrapper buildQueryWrapper(String title, String category, + String industry, String creator, String status) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 标题模糊查询 + if (isNotBlank(title)) { + queryWrapper.like(KnowledgeBase::getTitle, title.trim()); + } + + // 分类精确查询 + if (isNotBlank(category)) { + queryWrapper.eq(KnowledgeBase::getCategory, category.trim()); + } + + // 行业精确查询 + if (isNotBlank(industry)) { + queryWrapper.eq(KnowledgeBase::getIndustry, industry.trim()); + } + + // 创建人模糊查询 + if (isNotBlank(creator)) { + queryWrapper.like(KnowledgeBase::getCreator, creator.trim()); + } + + // 状态精确查询 + if (isNotBlank(status)) { + queryWrapper.eq(KnowledgeBase::getStatus, status.trim()); + } + + // 排序:置顶优先,然后按排序权重,最后按更新时间倒序 + queryWrapper.orderByDesc(KnowledgeBase::getIsTop) + .orderByDesc(KnowledgeBase::getSortOrder) + .orderByDesc(KnowledgeBase::getUpdateTime); + + return queryWrapper; + } + + /** + * 处理附件文件内容读取(用于调试和日志记录) + * 注意:此操作可能影响性能,生产环境建议移除或改为异步处理 + * + * @param knowledgeList 知识库列表 + */ + private void processAttachmentFiles(List knowledgeList) { + if (knowledgeList == null || knowledgeList.isEmpty()) { + return; + } + + knowledgeList.forEach(knowledgeBase -> { + String attachmentUrl = knowledgeBase.getAttachmentUrl(); + if (isNotBlank(attachmentUrl)) { + readAndLogAttachmentContent(knowledgeBase.getId(), attachmentUrl); + } + }); + } + + /** + * 读取并记录附件文件内容 + * + * @param knowledgeId 知识库ID + * @param attachmentUrl 附件URL + */ + private void readAndLogAttachmentContent(String knowledgeId, String attachmentUrl) { + try { + log.debug("知识库ID: {}, 附件URL: {}", knowledgeId, attachmentUrl); + + // 从URL中提取对象名(文件名) + String objectName = extractObjectNameFromUrl(attachmentUrl); + if (isBlank(objectName)) { + log.warn("无法从URL中提取对象名: {}", attachmentUrl); + return; + } + + log.debug("从MinIO读取文件: {}", objectName); + + // 只处理文本文件 + if (!isTextFile(objectName)) { + log.debug("文件 {} 不是文本文件,跳过内容读取", objectName); + return; + } + + // 从MinIO下载文件 + InputStream inputStream = minIOService.downloadFile(objectName); + if (inputStream == null) { + log.warn("无法从MinIO下载文件: {}", objectName); + return; + } + + // 读取文件内容预览 + String fileContent = readTextFileContent(inputStream, MAX_FILE_CONTENT_PREVIEW_LENGTH); + log.info("知识库ID: {}, 文件内容预览 (前{}字符):\n{}", + knowledgeId, MAX_FILE_CONTENT_PREVIEW_LENGTH, fileContent); + + } catch (Exception e) { + log.error("读取MinIO文件内容失败,知识库ID: {}, 错误: {}", knowledgeId, e.getMessage(), e); + } + } + + /** + * 构建成功结果 + * + * @param knowledgePage 分页结果 + * @return 结果Map + */ + private Map buildSuccessResult(Page knowledgePage) { + Map result = new HashMap<>(); + result.put("success", true); + result.put("message", "查询成功"); + result.put("data", knowledgePage.getRecords()); + result.put("total", knowledgePage.getTotal()); + result.put("current", knowledgePage.getCurrent()); + result.put("size", knowledgePage.getSize()); + result.put("pages", knowledgePage.getPages()); return result; } + /** + * 构建错误结果 + * + * @param message 错误消息 + * @return 结果Map + */ + private Map buildErrorResult(String message) { + Map result = new HashMap<>(); + result.put("success", false); + result.put("message", message); + return result; + } + + /** + * 判断字符串是否不为空 + * + * @param str 字符串 + * @return 是否不为空 + */ + private boolean isNotBlank(String str) { + return str != null && !str.trim().isEmpty(); + } + + /** + * 判断字符串是否为空 + * + * @param str 字符串 + * @return 是否为空 + */ + private boolean isBlank(String str) { + return str == null || str.trim().isEmpty(); + } + @Override public Map updateKnowledge(KnowledgeBase knowledgeBase) { Map result = new HashMap<>(); @@ -347,4 +523,99 @@ public class KnowledgeBaseServiceImpl extends ServiceImpl 0) { + // 获取最后一个非空部分作为对象名 + for (int i = parts.length - 1; i >= 0; i--) { + if (parts[i] != null && !parts[i].trim().isEmpty()) { + return parts[i]; + } + } + } + + return null; + } catch (Exception e) { + log.error("从URL提取对象名失败: {}", url, e); + return null; + } + } + + /** + * 判断文件是否为文本文件 + * + * @param fileName 文件名 + * @return 是否为文本文件 + */ + private boolean isTextFile(String fileName) { + if (isBlank(fileName)) { + return false; + } + + String lowerFileName = fileName.toLowerCase(); + for (String ext : TEXT_FILE_EXTENSIONS) { + if (lowerFileName.endsWith(ext)) { + return true; + } + } + + return false; + } + + /** + * 读取文本文件内容 + * + * @param inputStream 文件输入流 + * @param maxLength 最大读取长度(字符数) + * @return 文件内容(限制长度) + */ + private String readTextFileContent(InputStream inputStream, int maxLength) { + if (inputStream == null) { + return null; + } + + StringBuilder content = new StringBuilder(); + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + String line; + int totalLength = 0; + while ((line = reader.readLine()) != null && totalLength < maxLength) { + if (totalLength + line.length() > maxLength) { + // 如果加上这一行会超过限制,只读取部分内容 + int remaining = maxLength - totalLength; + content.append(line, 0, remaining); + content.append("\n...(内容已截断)"); + break; + } + content.append(line).append("\n"); + totalLength += line.length() + 1; // +1 for newline + } + } catch (Exception e) { + log.error("读取文件内容失败: {}", e.getMessage(), e); + return "读取文件内容失败: " + e.getMessage(); + } + + return content.toString(); + } }