diff --git a/ruoyi-admin/src/main/resources/mcp-server.json b/ruoyi-admin/src/main/resources/mcp-server.json index ab8ad189..36fef0ec 100644 --- a/ruoyi-admin/src/main/resources/mcp-server.json +++ b/ruoyi-admin/src/main/resources/mcp-server.json @@ -5,7 +5,7 @@ "args": [ "-y", "@modelcontextprotocol/server-filesystem", - "D:\\software" + "D:\\test" ] }, "search1api": { diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeFragment.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeFragment.java index c5c4052c..fae1b656 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeFragment.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeFragment.java @@ -45,7 +45,7 @@ public class KnowledgeFragment extends BaseEntity { /** * 片段索引下标 */ - private Long idx; + private Integer idx; /** * 文档内容 diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoUploadBo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoUploadBo.java new file mode 100644 index 00000000..d576b596 --- /dev/null +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeInfoUploadBo.java @@ -0,0 +1,16 @@ +package org.ruoyi.domain.bo; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author ageer + */ +@Data +public class KnowledgeInfoUploadBo { + + private String kid; + + private MultipartFile file; + +} diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachService.java index ee3640b5..6a3fca8d 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachService.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachService.java @@ -5,6 +5,7 @@ import org.ruoyi.domain.bo.KnowledgeAttachBo; import org.ruoyi.domain.vo.KnowledgeAttachVo; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.core.page.PageQuery; +import org.springframework.web.multipart.MultipartFile; import java.util.Collection; import java.util.List; @@ -46,4 +47,17 @@ public interface IKnowledgeAttachService { * 校验并批量删除知识库附件信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 删除知识附件 + */ + void removeKnowledgeAttach(String docId); + + /** + * 翻译文件 + * + * @param file 文件 + * @param targetLanguage 目标语音 + */ + String translationByFile(MultipartFile file, String targetLanguage); } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeInfoService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeInfoService.java index 4619d1d9..79ce870c 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeInfoService.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeInfoService.java @@ -2,6 +2,7 @@ package org.ruoyi.service; import org.ruoyi.domain.bo.KnowledgeInfoBo; +import org.ruoyi.domain.bo.KnowledgeInfoUploadBo; import org.ruoyi.domain.vo.KnowledgeInfoVo; import org.ruoyi.core.page.TableDataInfo; import org.ruoyi.core.page.PageQuery; @@ -46,4 +47,19 @@ public interface IKnowledgeInfoService { * 校验并批量删除知识库信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 新增知识库 + */ + void saveOne(KnowledgeInfoBo bo); + + /** + * 删除知识库 + */ + void removeKnowledge(String id); + + /** + * 上传附件 + */ + void upload(KnowledgeInfoUploadBo bo); } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachServiceImpl.java index a2194625..46869cb8 100644 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachServiceImpl.java +++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachServiceImpl.java @@ -9,13 +9,16 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.ruoyi.domain.vo.KnowledgeAttachVo; +import org.ruoyi.mapper.KnowledgeFragmentMapper; import org.springframework.stereotype.Service; import org.ruoyi.domain.bo.KnowledgeAttachBo; import org.ruoyi.domain.KnowledgeAttach; import org.ruoyi.mapper.KnowledgeAttachMapper; import org.ruoyi.service.IKnowledgeAttachService; +import org.springframework.web.multipart.MultipartFile; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Collection; @@ -31,6 +34,7 @@ import java.util.Collection; public class KnowledgeAttachServiceImpl implements IKnowledgeAttachService { private final KnowledgeAttachMapper baseMapper; + private final KnowledgeFragmentMapper fragmentMapper; /** * 查询知识库附件 @@ -111,4 +115,64 @@ public class KnowledgeAttachServiceImpl implements IKnowledgeAttachService { } return baseMapper.deleteBatchIds(ids) > 0; } + + @Override + public void removeKnowledgeAttach(String docId) { + Map map = new HashMap<>(); + map.put("doc_id",docId); + baseMapper.deleteByMap(map); + fragmentMapper.deleteByMap(map); + } + + @Override + public String translationByFile(MultipartFile file, String targetLanguage) { + /*String fileName = file.getOriginalFilename(); + String docType = fileName.substring(fileName.lastIndexOf(".")+1); + String content = ""; + ResourceLoader resourceLoader = resourceLoaderFactory.getLoaderByFileType(docType); + try { + content = resourceLoader.getContent(file.getInputStream()); + } catch (IOException e) { + throw new BaseException("该文件类型暂不支持!"); + } + // 翻译模型固定为gpt-4o-mini + String model = "gpt-4o-mini"; + ChatMessageBo chatMessageBo = new ChatMessageBo(); + chatMessageBo.setUserId(getUserId()); + chatMessageBo.setModelName(model); + chatMessageBo.setContent(content); + chatMessageBo.setDeductCost(0.01); + chatMessageBo.setTotalTokens(0); + OpenAiStreamClient openAiStreamClient = chatConfig.getOpenAiStreamClient(); + List messageList = new ArrayList<>(); + Message sysMessage = Message.builder().role(Message.Role.SYSTEM).content("你是一位精通各国语言的翻译大师\n" + + "\n" + + "请将用户输入词语翻译成{" + targetLanguage + "}\n" + + "\n" + + "==示例输出==\n" + + "**原文** : <这里显示要翻译的原文信息>\n" + + "**翻译** : <这里显示翻译之后的结果>\n" + + "**总结** : <这里是对关键信息一个总结>\n" + + "**提取的关键信息** : <这里返回关键信息>\n" + + "==示例结束==\n" + + "\n" + + "注意:请严格按示例进行输出,返回markdown格式").build(); + messageList.add(sysMessage); + Message message = Message.builder().role(Message.Role.USER).content(content).build(); + messageList.add(message); + ChatCompletionResponse chatCompletionResponse = null; + try { + ChatCompletion chatCompletion = ChatCompletion + .builder() + .messages(messageList) + .model(model) + .stream(false) + .build(); + chatCompletionResponse = openAiStreamClient.chatCompletion(chatCompletion); + }catch (Exception e) { + throw new BaseException("调用大模型失败,请检查密钥是否正确!"); + } + return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString();*/ + return "接口开发中!"; + } } diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeInfoServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeInfoServiceImpl.java deleted file mode 100644 index 254085a1..00000000 --- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeInfoServiceImpl.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.ruoyi.service.impl; - -import org.ruoyi.common.core.utils.MapstructUtils; -import org.ruoyi.common.core.utils.StringUtils; -import org.ruoyi.core.page.TableDataInfo; -import org.ruoyi.core.page.PageQuery; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import lombok.RequiredArgsConstructor; -import org.ruoyi.domain.vo.KnowledgeInfoVo; -import org.springframework.stereotype.Service; -import org.ruoyi.domain.bo.KnowledgeInfoBo; -import org.ruoyi.domain.KnowledgeInfo; -import org.ruoyi.mapper.KnowledgeInfoMapper; -import org.ruoyi.service.IKnowledgeInfoService; - -import java.util.List; -import java.util.Map; -import java.util.Collection; - -/** - * 知识库Service业务层处理 - * - * @author ageerle - * @date 2025-04-08 - */ -@RequiredArgsConstructor -@Service -public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService { - - private final KnowledgeInfoMapper baseMapper; - - /** - * 查询知识库 - */ - @Override - public KnowledgeInfoVo queryById(Long id){ - return baseMapper.selectVoById(id); - } - - /** - * 查询知识库列表 - */ - @Override - public TableDataInfo queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); - return TableDataInfo.build(result); - } - - /** - * 查询知识库列表 - */ - @Override - public List queryList(KnowledgeInfoBo bo) { - LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); - } - - private LambdaQueryWrapper buildQueryWrapper(KnowledgeInfoBo bo) { - Map params = bo.getParams(); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeInfo::getKid, bo.getKid()); - lqw.eq(bo.getUid() != null, KnowledgeInfo::getUid, bo.getUid()); - lqw.like(StringUtils.isNotBlank(bo.getKname()), KnowledgeInfo::getKname, bo.getKname()); - lqw.eq(bo.getShare() != null, KnowledgeInfo::getShare, bo.getShare()); - lqw.eq(StringUtils.isNotBlank(bo.getDescription()), KnowledgeInfo::getDescription, bo.getDescription()); - lqw.eq(StringUtils.isNotBlank(bo.getKnowledgeSeparator()), KnowledgeInfo::getKnowledgeSeparator, bo.getKnowledgeSeparator()); - lqw.eq(StringUtils.isNotBlank(bo.getQuestionSeparator()), KnowledgeInfo::getQuestionSeparator, bo.getQuestionSeparator()); - lqw.eq(bo.getOverlapChar() != null, KnowledgeInfo::getOverlapChar, bo.getOverlapChar()); - lqw.eq(bo.getRetrieveLimit() != null, KnowledgeInfo::getRetrieveLimit, bo.getRetrieveLimit()); - lqw.eq(bo.getTextBlockSize() != null, KnowledgeInfo::getTextBlockSize, bo.getTextBlockSize()); - lqw.eq(StringUtils.isNotBlank(bo.getVector()), KnowledgeInfo::getVector, bo.getVector()); - lqw.eq(StringUtils.isNotBlank(bo.getVectorModel()), KnowledgeInfo::getVectorModel, bo.getVectorModel()); - return lqw; - } - - /** - * 新增知识库 - */ - @Override - public Boolean insertByBo(KnowledgeInfoBo bo) { - KnowledgeInfo add = MapstructUtils.convert(bo, KnowledgeInfo.class); - validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; - if (flag) { - bo.setId(add.getId()); - } - return flag; - } - - /** - * 修改知识库 - */ - @Override - public Boolean updateByBo(KnowledgeInfoBo bo) { - KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class); - validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; - } - - /** - * 保存前的数据校验 - */ - private void validEntityBeforeSave(KnowledgeInfo entity){ - //TODO 做一些数据校验,如唯一约束 - } - - /** - * 批量删除知识库 - */ - @Override - public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - if(isValid){ - //TODO 做一些业务上的校验,判断是否需要校验 - } - return baseMapper.deleteBatchIds(ids) > 0; - } -} diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/IChatConfigService.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/IChatConfigService.java index e99c77a3..3f91faf5 100644 --- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/IChatConfigService.java +++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/IChatConfigService.java @@ -46,4 +46,10 @@ public interface IChatConfigService { * 校验并批量删除配置信息信息 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + + /** + * 查询系统参数 + */ + List getSysConfigValue(String category); } diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/ChatConfigServiceImpl.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/ChatConfigServiceImpl.java index b03a288e..353dd1c1 100644 --- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/ChatConfigServiceImpl.java +++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/ChatConfigServiceImpl.java @@ -129,4 +129,18 @@ public class ChatConfigServiceImpl implements ConfigService, IChatConfigService return baseMapper.deleteBatchIds(ids) > 0; } + /** + * 根据配置类型和配置key获取值 + * + * @param category + * @return + */ + @Override + public List getSysConfigValue(String category) { + ChatConfigBo bo = new ChatConfigBo(); + bo.setCategory(category); + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + } diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatConfigController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatConfigController.java index e44a0839..bf3569b3 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatConfigController.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatConfigController.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; +import org.ruoyi.common.core.service.ConfigService; import org.ruoyi.common.excel.utils.ExcelUtil; import org.ruoyi.common.idempotent.annotation.RepeatSubmit; import org.ruoyi.core.page.TableDataInfo; @@ -31,11 +32,14 @@ import org.ruoyi.common.log.enums.BusinessType; @Validated @RequiredArgsConstructor @RestController -@RequestMapping("/system/chatConfig") +@RequestMapping("/chat/config") public class ChatConfigController extends BaseController { private final IChatConfigService chatConfigService; + + private final ConfigService configService; + /** * 查询配置信息列表 */ @@ -102,4 +106,24 @@ public class ChatConfigController extends BaseController { @PathVariable Long[] ids) { return toAjax(chatConfigService.deleteWithValidByIds(List.of(ids), true)); } + + /** + * 根据参数键名查询系统参数值 + * + * @param configKey 参数Key + */ + @GetMapping(value = "/configKey/{configKey}") + public R getConfigKey(@PathVariable String configKey) { + return R.ok(configService.getConfigValue("sys",configKey)); + } + + /** + * 查询系统参数 + * + */ + @GetMapping(value = "/sysConfigKey") + public R> getSysConfigKey() { + return R.ok(chatConfigService.getSysConfigValue("sys")); + } + } diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatStoreController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatStoreController.java new file mode 100644 index 00000000..3886902a --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/chat/ChatStoreController.java @@ -0,0 +1,44 @@ +package org.ruoyi.chat.controller.chat; + +import lombok.RequiredArgsConstructor; +import org.ruoyi.common.core.domain.R; +import org.ruoyi.common.web.core.BaseController; +import org.ruoyi.domain.bo.ChatAppStoreBo; +import org.ruoyi.domain.vo.ChatAppStoreVo; +import org.ruoyi.service.IChatAppStoreService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import java.util.List; + + +/** + * 应用商店 + * + * @author Lion Li + * @date 2024-03-19 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/store") +public class ChatStoreController extends BaseController { + + private final IChatAppStoreService appStoreService; + + /** + * 应用商店 + */ + @GetMapping("/appList") + public R> appList(ChatAppStoreBo bo) { + return R.ok(appStoreService.queryList(bo)); + } + + /** + * 收藏应用 + */ + @PostMapping("/copyApp") + public R copyApp() { + return R.ok(); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java new file mode 100644 index 00000000..1a11d011 --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java @@ -0,0 +1,154 @@ +package org.ruoyi.chat.controller.knowledge; + +import cn.dev33.satoken.stp.StpUtil; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.ruoyi.common.core.domain.R; +import org.ruoyi.common.core.validate.AddGroup; +import org.ruoyi.common.excel.utils.ExcelUtil; +import org.ruoyi.common.log.annotation.Log; +import org.ruoyi.common.log.enums.BusinessType; +import org.ruoyi.common.satoken.utils.LoginHelper; +import org.ruoyi.common.web.core.BaseController; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.domain.bo.KnowledgeAttachBo; +import org.ruoyi.domain.bo.KnowledgeFragmentBo; +import org.ruoyi.domain.bo.KnowledgeInfoBo; +import org.ruoyi.domain.bo.KnowledgeInfoUploadBo; +import org.ruoyi.domain.vo.KnowledgeAttachVo; +import org.ruoyi.domain.vo.KnowledgeFragmentVo; +import org.ruoyi.domain.vo.KnowledgeInfoVo; +import org.ruoyi.service.IKnowledgeAttachService; +import org.ruoyi.service.IKnowledgeFragmentService; +import org.ruoyi.service.IKnowledgeInfoService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.util.List; + +/** + * @author ageer + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/knowledge") +public class KnowledgeController extends BaseController { + + private final IKnowledgeInfoService knowledgeInfoService; + + private final IKnowledgeAttachService attachService; + + private final IKnowledgeFragmentService fragmentService; + + /** + * 根据用户信息查询本地知识库 + */ + @GetMapping("/list") + public TableDataInfo list(KnowledgeInfoBo bo, PageQuery pageQuery) { + if (!StpUtil.isLogin()) { + throw new SecurityException("请先去登录!"); + } + bo.setUid(LoginHelper.getUserId()); + return knowledgeInfoService.queryPageList(bo, pageQuery); + } + + /** + * 新增知识库 + */ + @Log(title = "知识库", businessType = BusinessType.INSERT) + @PostMapping("/save") + public R save(@Validated(AddGroup.class) @RequestBody KnowledgeInfoBo bo) { + knowledgeInfoService.saveOne(bo); + return R.ok(); + } + + /** + * 删除知识库 + */ + @PostMapping("/remove/{id}") + public R remove(@PathVariable String id) { + knowledgeInfoService.removeKnowledge(id); + return R.ok("删除知识库成功!"); + } + + /** + * 修改知识库 + */ + @Log(title = "知识库", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + public R edit(@RequestBody KnowledgeInfoBo bo) { + return toAjax(knowledgeInfoService.updateByBo(bo)); + } + + /** + * 导出知识库列表 + */ + @Log(title = "知识库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(KnowledgeInfoBo bo, HttpServletResponse response) { + List list = knowledgeInfoService.queryList(bo); + ExcelUtil.exportExcel(list, "知识库", KnowledgeInfoVo.class, response); + } + + /** + * 查询知识附件信息 + */ + @GetMapping("/detail/{kid}") + public TableDataInfo attach(KnowledgeAttachBo bo, PageQuery pageQuery, @PathVariable String kid) { + bo.setKid(kid); + return attachService.queryPageList(bo, pageQuery); + } + + /** + * 上传知识库附件 + */ + @PostMapping(value = "/attach/upload") + public R upload(KnowledgeInfoUploadBo bo) { + knowledgeInfoService.upload(bo); + return R.ok("上传知识库附件成功!"); + } + + /** + * 获取知识库附件详细信息 + * + * @param id 主键 + */ + @GetMapping("attach/info/{id}") + public R getAttachInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(attachService.queryById(id)); + } + + /** + * 删除知识库附件 + */ + @PostMapping("attach/remove/{kid}") + public R removeAttach(@NotEmpty(message = "主键不能为空") + @PathVariable String kid) { + attachService.removeKnowledgeAttach(kid); + return R.ok(); + } + + + /** + * 查询知识片段 + */ + @GetMapping("/fragment/list/{docId}") + public TableDataInfo fragmentList(KnowledgeFragmentBo bo, PageQuery pageQuery, @PathVariable String docId) { + bo.setDocId(docId); + return fragmentService.queryPageList(bo, pageQuery); + } + + /** + * 上传文件翻译 + */ + @PostMapping("/translationByFile") + @ResponseBody + public String translationByFile(@RequestParam("file") MultipartFile file, String targetLanguage) { + return attachService.translationByFile(file, targetLanguage); + } +} diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java index 03fb18a5..6f7f977a 100644 --- a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/OpenAIServiceImpl.java @@ -20,6 +20,7 @@ import java.util.List; @Service @Slf4j public class OpenAIServiceImpl implements IChatService { + @Autowired private OpenAiStreamClient openAiStreamClient; diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java new file mode 100644 index 00000000..7a489a6b --- /dev/null +++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java @@ -0,0 +1,232 @@ +package org.ruoyi.chat.service.knowledge; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.RandomUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.ruoyi.chain.loader.ResourceLoader; +import org.ruoyi.chain.loader.ResourceLoaderFactory; +import org.ruoyi.common.core.domain.model.LoginUser; +import org.ruoyi.common.core.utils.MapstructUtils; +import org.ruoyi.common.core.utils.StringUtils; +import org.ruoyi.common.satoken.utils.LoginHelper; +import org.ruoyi.core.page.PageQuery; +import org.ruoyi.core.page.TableDataInfo; +import org.ruoyi.domain.KnowledgeAttach; +import org.ruoyi.domain.KnowledgeFragment; +import org.ruoyi.domain.KnowledgeInfo; +import org.ruoyi.domain.bo.KnowledgeInfoBo; +import org.ruoyi.domain.bo.KnowledgeInfoUploadBo; +import org.ruoyi.domain.vo.KnowledgeInfoVo; +import org.ruoyi.mapper.KnowledgeAttachMapper; +import org.ruoyi.mapper.KnowledgeFragmentMapper; +import org.ruoyi.mapper.KnowledgeInfoMapper; +import org.ruoyi.service.EmbeddingService; +import org.ruoyi.service.IKnowledgeInfoService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.*; + +/** + * 知识库Service业务层处理 + * + * @author ageerle + * @date 2025-04-08 + */ +@RequiredArgsConstructor +@Service +public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService { + + private final KnowledgeInfoMapper baseMapper; + + private final EmbeddingService embeddingService; + + private final ResourceLoaderFactory resourceLoaderFactory; + + private final KnowledgeFragmentMapper fragmentMapper; + + private final KnowledgeAttachMapper attachMapper; + + /** + * 查询知识库 + */ + @Override + public KnowledgeInfoVo queryById(Long id){ + return baseMapper.selectVoById(id); + } + + /** + * 查询知识库列表 + */ + @Override + public TableDataInfo queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询知识库列表 + */ + @Override + public List queryList(KnowledgeInfoBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(KnowledgeInfoBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeInfo::getKid, bo.getKid()); + lqw.eq(bo.getUid() != null, KnowledgeInfo::getUid, bo.getUid()); + lqw.like(StringUtils.isNotBlank(bo.getKname()), KnowledgeInfo::getKname, bo.getKname()); + lqw.eq(bo.getShare() != null, KnowledgeInfo::getShare, bo.getShare()); + lqw.eq(StringUtils.isNotBlank(bo.getDescription()), KnowledgeInfo::getDescription, bo.getDescription()); + lqw.eq(StringUtils.isNotBlank(bo.getKnowledgeSeparator()), KnowledgeInfo::getKnowledgeSeparator, bo.getKnowledgeSeparator()); + lqw.eq(StringUtils.isNotBlank(bo.getQuestionSeparator()), KnowledgeInfo::getQuestionSeparator, bo.getQuestionSeparator()); + lqw.eq(bo.getOverlapChar() != null, KnowledgeInfo::getOverlapChar, bo.getOverlapChar()); + lqw.eq(bo.getRetrieveLimit() != null, KnowledgeInfo::getRetrieveLimit, bo.getRetrieveLimit()); + lqw.eq(bo.getTextBlockSize() != null, KnowledgeInfo::getTextBlockSize, bo.getTextBlockSize()); + lqw.eq(StringUtils.isNotBlank(bo.getVector()), KnowledgeInfo::getVector, bo.getVector()); + lqw.eq(StringUtils.isNotBlank(bo.getVectorModel()), KnowledgeInfo::getVectorModel, bo.getVectorModel()); + return lqw; + } + + /** + * 新增知识库 + */ + @Override + public Boolean insertByBo(KnowledgeInfoBo bo) { + KnowledgeInfo add = MapstructUtils.convert(bo, KnowledgeInfo.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改知识库 + */ + @Override + public Boolean updateByBo(KnowledgeInfoBo bo) { + KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(KnowledgeInfo entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除知识库 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOne(KnowledgeInfoBo bo) { + KnowledgeInfo knowledgeInfo = MapstructUtils.convert(bo, KnowledgeInfo.class); + if (StringUtils.isBlank(bo.getKid())){ + String kid = RandomUtil.randomString(10); + if (knowledgeInfo != null) { + knowledgeInfo.setKid(kid); + knowledgeInfo.setUid(LoginHelper.getLoginUser().getUserId()); + } + baseMapper.insert(knowledgeInfo); + embeddingService.createSchema(String.valueOf(knowledgeInfo.getId())); + }else { + baseMapper.updateById(knowledgeInfo); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void removeKnowledge(String id) { + Map map = new HashMap<>(); + map.put("kid",id); + List knowledgeInfoList = baseMapper.selectVoByMap(map); + check(knowledgeInfoList); + // 删除向量库信息 + knowledgeInfoList.forEach(knowledgeInfoVo -> { + embeddingService.removeByKid(String.valueOf(knowledgeInfoVo.getId())); + }); + // 删除附件和知识片段 + fragmentMapper.deleteByMap(map); + attachMapper.deleteByMap(map); + // 删除知识库 + baseMapper.deleteByMap(map); + } + + @Override + public void upload(KnowledgeInfoUploadBo bo) { + storeContent(bo.getFile(), bo.getKid()); + } + + public void storeContent(MultipartFile file, String kid) { + String fileName = file.getOriginalFilename(); + List chunkList = new ArrayList<>(); + KnowledgeAttach knowledgeAttach = new KnowledgeAttach(); + knowledgeAttach.setKid(kid); + String docId = RandomUtil.randomString(10); + knowledgeAttach.setDocId(docId); + knowledgeAttach.setDocName(fileName); + knowledgeAttach.setDocType(fileName.substring(fileName.lastIndexOf(".")+1)); + String content = ""; + ResourceLoader resourceLoader = resourceLoaderFactory.getLoaderByFileType(knowledgeAttach.getDocType()); + List fids = new ArrayList<>(); + try { + content = resourceLoader.getContent(file.getInputStream()); + chunkList = resourceLoader.getChunkList(content, kid); + List knowledgeFragmentList = new ArrayList<>(); + if (CollUtil.isNotEmpty(chunkList)) { + for (int i = 0; i < chunkList.size(); i++) { + String fid = RandomUtil.randomString(16); + fids.add(fid); + KnowledgeFragment knowledgeFragment = new KnowledgeFragment(); + knowledgeFragment.setKid(kid); + knowledgeFragment.setDocId(docId); + knowledgeFragment.setFid(fid); + knowledgeFragment.setIdx(i); + knowledgeFragment.setContent(chunkList.get(i)); + knowledgeFragment.setCreateTime(new Date()); + knowledgeFragmentList.add(knowledgeFragment); + } + } + fragmentMapper.insertBatch(knowledgeFragmentList); + } catch (IOException e) { + e.printStackTrace(); + } + knowledgeAttach.setContent(content); + knowledgeAttach.setCreateTime(new Date()); + attachMapper.insert(knowledgeAttach); + embeddingService.storeEmbeddings(chunkList,kid,docId,fids); + } + + + public void check(List knowledgeInfoList){ + LoginUser loginUser = LoginHelper.getLoginUser(); + for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) { + if(!knowledgeInfoVo.getUid().equals(loginUser.getUserId())){ + throw new SecurityException("权限不足"); + } + } + } + +}