diff --git a/ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java b/ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java
index df57f7ed..cc65b7b4 100644
--- a/ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java
+++ b/ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java
@@ -3,6 +3,7 @@ package org.ruoyi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
+import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
@@ -12,6 +13,7 @@ import org.springframework.scheduling.annotation.EnableScheduling;
*/
@SpringBootApplication
@EnableScheduling
+@EnableAsync
public class RuoYiAIApplication {
public static void main(String[] args) {
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml b/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
index 83eac39a..4b4c1fe6 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
@@ -114,6 +114,10 @@
commons-io
2.17.0
+
+ org.ruoyi
+ ruoyi-system-api
+
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/constant/DealStatus.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/constant/DealStatus.java
index aadf19a0..273b1b04 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/constant/DealStatus.java
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/constant/DealStatus.java
@@ -11,6 +11,8 @@ public class DealStatus {
public static final Integer STATUS_20 = 20;
//已结束
public static final Integer STATUS_30 = 30;
+ //处理失败
+ public static final Integer STATUS_40 = 40;
}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeAttachPic.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeAttachPic.java
new file mode 100644
index 00000000..f0a28c7e
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeAttachPic.java
@@ -0,0 +1,81 @@
+package org.ruoyi.domain;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+import org.ruoyi.core.domain.BaseEntity;
+
+/**
+ * 知识库附件图片列对象 knowledge_attach_pic
+ *
+ * @author Albert
+ * @date 2025-05-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("knowledge_attach_pic")
+public class KnowledgeAttachPic extends BaseEntity {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @TableId(value = "id")
+ private Long id;
+
+ /**
+ * 知识库id
+ */
+ private String kid;
+
+ /**
+ * 附件id
+ */
+ private String aid;
+
+ /**
+ * 文档名称
+ */
+ private String docName;
+
+ /**
+ * 文档类型
+ */
+ private String docType;
+
+ /**
+ * 文档内容
+ */
+ private String content;
+
+ /**
+ * 所在页数
+ */
+ private Integer pageNum;
+
+ /**
+ * 所在页index
+ */
+ private Integer indexNum;
+
+ /**
+ * 分析图片状态10未开始,20进行中,30已完成
+ */
+ private Integer picAnysStatus;
+
+ /**
+ * 对象存储主键
+ */
+ private Long ossId;
+
+ /**
+ * 备注
+ */
+ private String remark;
+
+
+}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeAttachPicBo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeAttachPicBo.java
new file mode 100644
index 00000000..6fe9e3ce
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeAttachPicBo.java
@@ -0,0 +1,90 @@
+package org.ruoyi.domain.bo;
+
+import org.ruoyi.common.core.validate.AddGroup;
+import org.ruoyi.common.core.validate.EditGroup;
+import org.ruoyi.core.domain.BaseEntity;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+import org.ruoyi.domain.KnowledgeAttachPic;
+
+/**
+ * 知识库附件图片列业务对象 knowledge_attach_pic
+ *
+ * @author Albert
+ * @date 2025-05-15
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = KnowledgeAttachPic.class, reverseConvertGenerate = false)
+public class KnowledgeAttachPicBo extends BaseEntity {
+
+ /**
+ * 主键
+ */
+ @NotNull(message = "主键不能为空", groups = {EditGroup.class})
+ private Long id;
+
+ /**
+ * 知识库id
+ */
+ @NotBlank(message = "知识库id不能为空", groups = {AddGroup.class, EditGroup.class})
+ private String kid;
+
+ /**
+ * 附件id
+ */
+ @NotBlank(message = "附件id不能为空", groups = {AddGroup.class, EditGroup.class})
+ private String aid;
+
+ /**
+ * 文档名称
+ */
+ @NotBlank(message = "文档名称不能为空", groups = {AddGroup.class, EditGroup.class})
+ private String docName;
+
+ /**
+ * 文档类型
+ */
+ @NotBlank(message = "文档类型不能为空", groups = {AddGroup.class, EditGroup.class})
+ private String docType;
+
+ /**
+ * 文档内容
+ */
+ @NotBlank(message = "文档内容不能为空", groups = {AddGroup.class, EditGroup.class})
+ private String content;
+
+ /**
+ * 所在页数
+ */
+ @NotNull(message = "所在页数不能为空", groups = {AddGroup.class, EditGroup.class})
+ private Integer pageNum;
+
+ /**
+ * 所在页index
+ */
+ @NotNull(message = "所在页index不能为空", groups = {AddGroup.class, EditGroup.class})
+ private Integer indexNum;
+
+ /**
+ * 分析图片状态10未开始,20进行中,30已完成
+ */
+ @NotNull(message = "分析图片状态10未开始,20进行中,30已完成不能为空", groups = {AddGroup.class,
+ EditGroup.class})
+ private Integer picAnysStatus;
+
+ /**
+ * 对象存储主键
+ */
+ @NotNull(message = "对象存储主键不能为空", groups = {AddGroup.class, EditGroup.class})
+ private Long ossId;
+
+ /**
+ * 备注
+ */
+ @NotBlank(message = "备注不能为空", groups = {AddGroup.class, EditGroup.class})
+ private String remark;
+
+}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeAttachPicVo.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeAttachPicVo.java
new file mode 100644
index 00000000..8972d807
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeAttachPicVo.java
@@ -0,0 +1,92 @@
+package org.ruoyi.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import java.io.Serial;
+import java.io.Serializable;
+import org.ruoyi.domain.KnowledgeAttachPic;
+
+
+/**
+ * 知识库附件图片列视图对象 knowledge_attach_pic
+ *
+ * @author Albert
+ * @date 2025-05-15
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = KnowledgeAttachPic.class)
+public class KnowledgeAttachPicVo implements Serializable {
+
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 主键
+ */
+ @ExcelProperty(value = "主键")
+ private Long id;
+
+ /**
+ * 知识库id
+ */
+ @ExcelProperty(value = "知识库id")
+ private String kid;
+
+ /**
+ * 附件id
+ */
+ @ExcelProperty(value = "附件id")
+ private String aid;
+
+ /**
+ * 文档名称
+ */
+ @ExcelProperty(value = "文档名称")
+ private String docName;
+
+ /**
+ * 文档类型
+ */
+ @ExcelProperty(value = "文档类型")
+ private String docType;
+
+ /**
+ * 文档内容
+ */
+ @ExcelProperty(value = "文档内容")
+ private String content;
+
+ /**
+ * 所在页数
+ */
+ @ExcelProperty(value = "所在页数")
+ private Integer pageNum;
+
+ /**
+ * 所在页index
+ */
+ @ExcelProperty(value = "所在页index")
+ private Integer indexNum;
+
+ /**
+ * 分析图片状态10未开始,20进行中,30已完成
+ */
+ @ExcelProperty(value = "分析图片状态10未开始,20进行中,30已完成")
+ private Integer picAnysStatus;
+
+ /**
+ * 对象存储主键
+ */
+ @ExcelProperty(value = "对象存储主键")
+ private Long ossId;
+
+ /**
+ * 备注
+ */
+ @ExcelProperty(value = "备注")
+ private String remark;
+
+}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/mapper/KnowledgeAttachPicMapper.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/mapper/KnowledgeAttachPicMapper.java
new file mode 100644
index 00000000..b1db81c6
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/mapper/KnowledgeAttachPicMapper.java
@@ -0,0 +1,15 @@
+package org.ruoyi.mapper;
+
+import org.ruoyi.core.mapper.BaseMapperPlus;
+import org.ruoyi.domain.KnowledgeAttachPic;
+import org.ruoyi.domain.vo.KnowledgeAttachPicVo;
+
+/**
+ * 知识库附件图片列Mapper接口
+ *
+ * @author Albert
+ * @date 2025-05-15
+ */
+public interface KnowledgeAttachPicMapper extends BaseMapperPlus {
+
+}
\ No newline at end of file
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachPicService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachPicService.java
new file mode 100644
index 00000000..7cdc1eb6
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachPicService.java
@@ -0,0 +1,47 @@
+package org.ruoyi.service;
+
+import java.util.Collection;
+import java.util.List;
+import org.ruoyi.core.page.PageQuery;
+import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.domain.bo.KnowledgeAttachPicBo;
+import org.ruoyi.domain.vo.KnowledgeAttachPicVo;
+
+/**
+ * 知识库附件图片列Service接口
+ *
+ * @author Albert
+ * @date 2025-05-15
+ */
+public interface IKnowledgeAttachPicService {
+
+ /**
+ * 查询知识库附件图片列
+ */
+ KnowledgeAttachPicVo queryById(Long id);
+
+ /**
+ * 查询知识库附件图片列列表
+ */
+ TableDataInfo queryPageList(KnowledgeAttachPicBo bo, PageQuery pageQuery);
+
+ /**
+ * 查询知识库附件图片列列表
+ */
+ List queryList(KnowledgeAttachPicBo bo);
+
+ /**
+ * 新增知识库附件图片列
+ */
+ Boolean insertByBo(KnowledgeAttachPicBo bo);
+
+ /**
+ * 修改知识库附件图片列
+ */
+ Boolean updateByBo(KnowledgeAttachPicBo bo);
+
+ /**
+ * 校验并批量删除知识库附件图片列信息
+ */
+ Boolean deleteWithValidByIds(Collection ids, Boolean isValid);
+}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachPicServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachPicServiceImpl.java
new file mode 100644
index 00000000..4b088e42
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachPicServiceImpl.java
@@ -0,0 +1,123 @@
+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.KnowledgeAttachPic;
+import org.ruoyi.domain.bo.KnowledgeAttachPicBo;
+import org.ruoyi.domain.vo.KnowledgeAttachPicVo;
+import org.ruoyi.mapper.KnowledgeAttachPicMapper;
+import org.ruoyi.service.IKnowledgeAttachPicService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 知识库附件图片列Service业务层处理
+ *
+ * @author ageerle
+ * @date 2025-05-15
+ */
+@RequiredArgsConstructor
+@Service
+public class KnowledgeAttachPicServiceImpl implements IKnowledgeAttachPicService {
+
+ private final KnowledgeAttachPicMapper baseMapper;
+
+ /**
+ * 查询知识库附件图片列
+ */
+ @Override
+ public KnowledgeAttachPicVo queryById(Long id) {
+ return baseMapper.selectVoById(id);
+ }
+
+ /**
+ * 查询知识库附件图片列列表
+ */
+ @Override
+ public TableDataInfo queryPageList(KnowledgeAttachPicBo bo,
+ PageQuery pageQuery) {
+ LambdaQueryWrapper lqw = buildQueryWrapper(bo);
+ Page result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ return TableDataInfo.build(result);
+ }
+
+ /**
+ * 查询知识库附件图片列列表
+ */
+ @Override
+ public List queryList(KnowledgeAttachPicBo bo) {
+ LambdaQueryWrapper lqw = buildQueryWrapper(bo);
+ return baseMapper.selectVoList(lqw);
+ }
+
+ private LambdaQueryWrapper buildQueryWrapper(KnowledgeAttachPicBo bo) {
+ Map params = bo.getParams();
+ LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
+ lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeAttachPic::getKid, bo.getKid());
+ lqw.eq(StringUtils.isNotBlank(bo.getAid()), KnowledgeAttachPic::getAid, bo.getAid());
+ lqw.like(StringUtils.isNotBlank(bo.getDocName()), KnowledgeAttachPic::getDocName,
+ bo.getDocName());
+ lqw.eq(StringUtils.isNotBlank(bo.getDocType()), KnowledgeAttachPic::getDocType,
+ bo.getDocType());
+ lqw.eq(StringUtils.isNotBlank(bo.getContent()), KnowledgeAttachPic::getContent,
+ bo.getContent());
+ lqw.eq(bo.getPageNum() != null, KnowledgeAttachPic::getPageNum, bo.getPageNum());
+ lqw.eq(bo.getIndexNum() != null, KnowledgeAttachPic::getIndexNum, bo.getIndexNum());
+ lqw.eq(bo.getPicAnysStatus() != null, KnowledgeAttachPic::getPicAnysStatus,
+ bo.getPicAnysStatus());
+ lqw.eq(bo.getOssId() != null, KnowledgeAttachPic::getOssId, bo.getOssId());
+ return lqw;
+ }
+
+ /**
+ * 新增知识库附件图片列
+ */
+ @Override
+ public Boolean insertByBo(KnowledgeAttachPicBo bo) {
+ KnowledgeAttachPic add = MapstructUtils.convert(bo, KnowledgeAttachPic.class);
+ validEntityBeforeSave(add);
+ boolean flag = baseMapper.insert(add) > 0;
+ if (flag) {
+ bo.setId(add.getId());
+ }
+ return flag;
+ }
+
+ /**
+ * 修改知识库附件图片列
+ */
+ @Override
+ public Boolean updateByBo(KnowledgeAttachPicBo bo) {
+ KnowledgeAttachPic update = MapstructUtils.convert(bo, KnowledgeAttachPic.class);
+ validEntityBeforeSave(update);
+ return baseMapper.updateById(update) > 0;
+ }
+
+ /**
+ * 保存前的数据校验
+ */
+ private void validEntityBeforeSave(KnowledgeAttachPic 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-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 46869cb8..9bd2c45b 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
@@ -1,5 +1,7 @@
package org.ruoyi.service.impl;
+import cn.hutool.core.util.ObjectUtil;
+import java.util.stream.Collectors;
import org.ruoyi.common.core.utils.MapstructUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.core.page.TableDataInfo;
@@ -8,8 +10,11 @@ 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.KnowledgeAttachPic;
import org.ruoyi.domain.vo.KnowledgeAttachVo;
+import org.ruoyi.mapper.KnowledgeAttachPicMapper;
import org.ruoyi.mapper.KnowledgeFragmentMapper;
+import org.ruoyi.system.service.ISysOssService;
import org.springframework.stereotype.Service;
import org.ruoyi.domain.bo.KnowledgeAttachBo;
@@ -33,99 +38,130 @@ import java.util.Collection;
@Service
public class KnowledgeAttachServiceImpl implements IKnowledgeAttachService {
- private final KnowledgeAttachMapper baseMapper;
- private final KnowledgeFragmentMapper fragmentMapper;
+ private final KnowledgeAttachMapper baseMapper;
+ private final KnowledgeFragmentMapper fragmentMapper;
- /**
- * 查询知识库附件
- */
- @Override
- public KnowledgeAttachVo queryById(Long id){
- return baseMapper.selectVoById(id);
+ private final ISysOssService ossService;
+
+ private final KnowledgeAttachPicMapper picMapper;
+
+ /**
+ * 查询知识库附件
+ */
+ @Override
+ public KnowledgeAttachVo queryById(Long id) {
+ return baseMapper.selectVoById(id);
+ }
+
+ /**
+ * 查询知识库附件列表
+ */
+ @Override
+ public TableDataInfo queryPageList(KnowledgeAttachBo bo, PageQuery pageQuery) {
+ LambdaQueryWrapper lqw = buildQueryWrapper(bo);
+ Page result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ return TableDataInfo.build(result);
+ }
+
+ /**
+ * 查询知识库附件列表
+ */
+ @Override
+ public List queryList(KnowledgeAttachBo bo) {
+ LambdaQueryWrapper lqw = buildQueryWrapper(bo);
+ return baseMapper.selectVoList(lqw);
+ }
+
+ private LambdaQueryWrapper buildQueryWrapper(KnowledgeAttachBo bo) {
+ Map params = bo.getParams();
+ LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
+ lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeAttach::getKid, bo.getKid());
+ lqw.eq(StringUtils.isNotBlank(bo.getDocId()), KnowledgeAttach::getDocId, bo.getDocId());
+ lqw.like(StringUtils.isNotBlank(bo.getDocName()), KnowledgeAttach::getDocName, bo.getDocName());
+ lqw.eq(StringUtils.isNotBlank(bo.getDocType()), KnowledgeAttach::getDocType, bo.getDocType());
+ lqw.eq(StringUtils.isNotBlank(bo.getContent()), KnowledgeAttach::getContent, bo.getContent());
+ return lqw;
+ }
+
+ /**
+ * 新增知识库附件
+ */
+ @Override
+ public Boolean insertByBo(KnowledgeAttachBo bo) {
+ KnowledgeAttach add = MapstructUtils.convert(bo, KnowledgeAttach.class);
+ validEntityBeforeSave(add);
+ boolean flag = baseMapper.insert(add) > 0;
+ if (flag) {
+ bo.setId(add.getId());
}
+ return flag;
+ }
- /**
- * 查询知识库附件列表
- */
- @Override
- public TableDataInfo queryPageList(KnowledgeAttachBo bo, PageQuery pageQuery) {
- LambdaQueryWrapper lqw = buildQueryWrapper(bo);
- Page result = baseMapper.selectVoPage(pageQuery.build(), lqw);
- return TableDataInfo.build(result);
+ /**
+ * 修改知识库附件
+ */
+ @Override
+ public Boolean updateByBo(KnowledgeAttachBo bo) {
+ KnowledgeAttach update = MapstructUtils.convert(bo, KnowledgeAttach.class);
+ validEntityBeforeSave(update);
+ return baseMapper.updateById(update) > 0;
+ }
+
+ /**
+ * 保存前的数据校验
+ */
+ private void validEntityBeforeSave(KnowledgeAttach entity) {
+ //TODO 做一些数据校验,如唯一约束
+ }
+
+ /**
+ * 批量删除知识库附件
+ */
+ @Override
+ public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
+ if (isValid) {
+ //TODO 做一些业务上的校验,判断是否需要校验
}
+ return baseMapper.deleteBatchIds(ids) > 0;
+ }
- /**
- * 查询知识库附件列表
- */
- @Override
- public List queryList(KnowledgeAttachBo bo) {
- LambdaQueryWrapper lqw = buildQueryWrapper(bo);
- return baseMapper.selectVoList(lqw);
+ @Override
+ public void removeKnowledgeAttach(String docId) {
+ Map map = new HashMap<>();
+ map.put("doc_id", docId);
+ List knowledgeAttachVos = baseMapper.selectVoByMap(map);
+ if (ObjectUtil.isNotEmpty(knowledgeAttachVos)) {
+ Collection ossIds = knowledgeAttachVos.stream()
+ .map(KnowledgeAttachVo::getOssId)
+ .collect(Collectors.toList());
+ //删除oss
+ ossService.deleteWithValidByIds(ossIds, false);
+ //删除图片oss
+ List knowledgeAttachPics = picMapper.selectList(
+ new LambdaQueryWrapper()
+ .in(KnowledgeAttachPic::getKid,
+ knowledgeAttachVos.stream().map(KnowledgeAttachVo::getKid)
+ .collect(Collectors.toList()))
+ .in(KnowledgeAttachPic::getAid,
+ knowledgeAttachVos.stream().map(KnowledgeAttachVo::getId)
+ .collect(Collectors.toList()))
+ );
+ if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) {
+ Collection tossIds = knowledgeAttachPics.stream()
+ .map(KnowledgeAttachPic::getOssId)
+ .collect(Collectors.toList());
+ ossService.deleteWithValidByIds(tossIds, false);
+ List collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId)
+ .collect(Collectors.toList());
+ picMapper.deleteByIds(collect);
+ }
}
+ baseMapper.deleteByMap(map);
+ fragmentMapper.deleteByMap(map);
+ }
- private LambdaQueryWrapper buildQueryWrapper(KnowledgeAttachBo bo) {
- Map params = bo.getParams();
- LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
- lqw.eq(StringUtils.isNotBlank(bo.getKid()), KnowledgeAttach::getKid, bo.getKid());
- lqw.eq(StringUtils.isNotBlank(bo.getDocId()), KnowledgeAttach::getDocId, bo.getDocId());
- lqw.like(StringUtils.isNotBlank(bo.getDocName()), KnowledgeAttach::getDocName, bo.getDocName());
- lqw.eq(StringUtils.isNotBlank(bo.getDocType()), KnowledgeAttach::getDocType, bo.getDocType());
- lqw.eq(StringUtils.isNotBlank(bo.getContent()), KnowledgeAttach::getContent, bo.getContent());
- return lqw;
- }
-
- /**
- * 新增知识库附件
- */
- @Override
- public Boolean insertByBo(KnowledgeAttachBo bo) {
- KnowledgeAttach add = MapstructUtils.convert(bo, KnowledgeAttach.class);
- validEntityBeforeSave(add);
- boolean flag = baseMapper.insert(add) > 0;
- if (flag) {
- bo.setId(add.getId());
- }
- return flag;
- }
-
- /**
- * 修改知识库附件
- */
- @Override
- public Boolean updateByBo(KnowledgeAttachBo bo) {
- KnowledgeAttach update = MapstructUtils.convert(bo, KnowledgeAttach.class);
- validEntityBeforeSave(update);
- return baseMapper.updateById(update) > 0;
- }
-
- /**
- * 保存前的数据校验
- */
- private void validEntityBeforeSave(KnowledgeAttach entity){
- //TODO 做一些数据校验,如唯一约束
- }
-
- /**
- * 批量删除知识库附件
- */
- @Override
- public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
- if(isValid){
- //TODO 做一些业务上的校验,判断是否需要校验
- }
- 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) {
+ @Override
+ public String translationByFile(MultipartFile file, String targetLanguage) {
/*String fileName = file.getOriginalFilename();
String docType = fileName.substring(fileName.lastIndexOf(".")+1);
String content = "";
@@ -173,6 +209,6 @@ public class KnowledgeAttachServiceImpl implements IKnowledgeAttachService {
throw new BaseException("调用大模型失败,请检查密钥是否正确!");
}
return chatCompletionResponse.getChoices().get(0).getMessage().getContent().toString();*/
- return "接口开发中!";
- }
+ return "接口开发中!";
+ }
}
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java
index 55ce87ed..29aae8eb 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java
@@ -4,6 +4,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
+import lombok.AllArgsConstructor;
+import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
@@ -23,16 +25,19 @@ import org.springframework.web.multipart.MultipartFile;
/**
* PDF图片提取服务实现类
*/
-@Service
+//@Service
@Slf4j
-public class PdfImageExtractServiceImpl implements PdfImageExtractService {
+@Data
+@AllArgsConstructor
+//public class PdfImageExtractServiceImpl implements PdfImageExtractService {
+public class PdfImageExtractServiceImpl {
- @Value("${pdf.extract.service.url}")
+// @Value("${pdf.extract.service.url}")
private String serviceUrl;
- @Value("${pdf.extract.ai-api.url}")
+// @Value("${pdf.extract.ai-api.url}")
private String aiApiUrl;
- @Value("${pdf.extract.ai-api.key}")
- private String aiApiKey ;
+// @Value("${pdf.extract.ai-api.key}")
+ private String aiApiKey;
private final OkHttpClient client = new Builder()
.connectTimeout(100, TimeUnit.SECONDS)
@@ -43,7 +48,7 @@ public class PdfImageExtractServiceImpl implements PdfImageExtractService {
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
- @Override
+// @Override
public byte[] extractImages(MultipartFile pdfFile, String imageFormat, boolean allowDuplicates)
throws IOException {
// 构建multipart请求
@@ -77,7 +82,7 @@ public class PdfImageExtractServiceImpl implements PdfImageExtractService {
* @return 文件内容结果列表
* @throws IOException 如果API调用过程中发生错误
*/
- @Override
+// @Override
public List dealFileContent(String[] unzip) throws IOException {
List results = new ArrayList<>();
int i = 0;
@@ -110,6 +115,7 @@ public class PdfImageExtractServiceImpl implements PdfImageExtractService {
// 执行请求
try {
log.info("=============call=" + ++i);
+
Response response = client.newCall(request).execute();
log.info("=============response=" + response);
if (!response.isSuccessful()) {
@@ -126,11 +132,10 @@ public class PdfImageExtractServiceImpl implements PdfImageExtractService {
throw new RuntimeException(e);
}
}
-
return results;
}
- @Override
+// @Override
public List extractImages(MultipartFile file) throws IOException {
String format = "png";
boolean allowDuplicates = true;
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java
index b5f79628..7e1f6f9b 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java
@@ -97,7 +97,7 @@ public class VectorStoreServiceImpl implements VectorStoreService {
for (int i = 0; i < chunkList.size(); i++) {
Map dataSchema = new HashMap<>();
dataSchema.put("kid", storeEmbeddingBo.getKid());
- dataSchema.put("docId", storeEmbeddingBo.getKid());
+ dataSchema.put("docId", storeEmbeddingBo.getDocId());
dataSchema.put("fid", storeEmbeddingBo.getFids().get(i));
Embedding embedding = embeddingModel.embed(chunkList.get(i)).content();
TextSegment segment = TextSegment.from(chunkList.get(i));
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java
index 7ae1b2b1..3c161311 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java
@@ -11,6 +11,8 @@ import java.util.Base64;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import org.springframework.mock.web.MockMultipartFile;
+import org.springframework.web.multipart.MultipartFile;
/**
* ZIP文件处理工具类
@@ -92,4 +94,90 @@ public class ZipUtils {
}
return base64Contents.toArray(new String[0]);
}
+
+ /**
+ * 解压ZIP文件并返回MultipartFile数组
+ *
+ * @param zipData ZIP文件的字节数组
+ * @return MultipartFile数组
+ * @throws IOException 如果解压过程中发生错误
+ */
+ public static MultipartFile[] unzipToMultipartFiles(byte[] zipData) throws IOException {
+ List multipartFiles = new ArrayList<>();
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(zipData);
+ ZipInputStream zis = new ZipInputStream(bis)) {
+
+ ZipEntry zipEntry;
+ while ((zipEntry = zis.getNextEntry()) != null) {
+ if (!zipEntry.isDirectory()) {
+ // 读取文件内容到内存
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[4096];
+ int read;
+ while ((read = zis.read(buffer)) != -1) {
+ baos.write(buffer, 0, read);
+ }
+
+ // 创建MultipartFile对象
+ String fileName = zipEntry.getName();
+ byte[] content = baos.toByteArray();
+ String contentType = determineContentType(fileName);
+
+ MultipartFile multipartFile = new MockMultipartFile(
+ fileName, // 文件名
+ fileName, // 原始文件名
+ contentType, // 内容类型
+ content // 文件内容
+ );
+
+ multipartFiles.add(multipartFile);
+ }
+ zis.closeEntry();
+ }
+ }
+ return multipartFiles.toArray(new MultipartFile[0]);
+ }
+
+ /**
+ * 根据文件名确定内容类型
+ *
+ * @param fileName 文件名
+ * @return 内容类型
+ */
+ private static String determineContentType(String fileName) {
+ String extension = "";
+ int i = fileName.lastIndexOf('.');
+ if (i > 0) {
+ extension = fileName.substring(i + 1).toLowerCase();
+ }
+
+ switch (extension) {
+ case "txt":
+ return "text/plain";
+ case "html":
+ case "htm":
+ return "text/html";
+ case "pdf":
+ return "application/pdf";
+ case "jpg":
+ case "jpeg":
+ return "image/jpeg";
+ case "png":
+ return "image/png";
+ case "gif":
+ return "image/gif";
+ case "doc":
+ case "docx":
+ return "application/msword";
+ case "xls":
+ case "xlsx":
+ return "application/vnd.ms-excel";
+ case "xml":
+ return "application/xml";
+ case "json":
+ return "application/json";
+ default:
+ return "application/octet-stream";
+ }
+ }
}
\ No newline at end of file
diff --git a/ruoyi-modules-api/ruoyi-system-api/pom.xml b/ruoyi-modules-api/ruoyi-system-api/pom.xml
index 69fe0cc6..1f3deac4 100644
--- a/ruoyi-modules-api/ruoyi-system-api/pom.xml
+++ b/ruoyi-modules-api/ruoyi-system-api/pom.xml
@@ -17,5 +17,11 @@
17
UTF-8
+
+
+ org.springframework
+ spring-test
+
+
diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysLogininforService.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysLogininforService.java
index 2b2d0556..8f3f50c6 100644
--- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysLogininforService.java
+++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysLogininforService.java
@@ -1,5 +1,6 @@
package org.ruoyi.system.service;
+import org.ruoyi.common.log.event.LogininforEvent;
import org.ruoyi.core.page.PageQuery;
import org.ruoyi.core.page.TableDataInfo;
import org.ruoyi.system.domain.bo.SysLogininforBo;
@@ -44,4 +45,6 @@ public interface ISysLogininforService {
* 清空系统登录日志
*/
void cleanLogininfor();
+
+ void recordLogininfor(LogininforEvent logininforEvent);
}
diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOperLogService.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOperLogService.java
index 65fae975..e3a39b8c 100644
--- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOperLogService.java
+++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOperLogService.java
@@ -1,5 +1,6 @@
package org.ruoyi.system.service;
+import org.ruoyi.common.log.event.OperLogEvent;
import org.ruoyi.core.page.PageQuery;
import org.ruoyi.core.page.TableDataInfo;
import org.ruoyi.system.domain.bo.SysOperLogBo;
@@ -51,4 +52,5 @@ public interface ISysOperLogService {
* 清空操作日志
*/
void cleanOperLog();
+ void recordOper(OperLogEvent operLogEvent);
}
diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOssService.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOssService.java
index 1461f0c5..a9ff4952 100644
--- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOssService.java
+++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOssService.java
@@ -18,16 +18,20 @@ import java.util.List;
*/
public interface ISysOssService {
- TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery);
+ TableDataInfo queryPageList(SysOssBo sysOss, PageQuery pageQuery);
- List listByIds(Collection ossIds);
+ List listByIds(Collection ossIds);
- SysOssVo getById(Long ossId);
+ SysOssVo getById(Long ossId);
- SysOssVo upload(MultipartFile file);
+ SysOssVo upload(MultipartFile file);
- void download(Long ossId, HttpServletResponse response) throws IOException;
+ void download(Long ossId, HttpServletResponse response) throws IOException;
- Boolean deleteWithValidByIds(Collection ids, Boolean isValid);
+ MultipartFile downloadByFile(Long ossId) throws IOException;
+
+ String downloadByByte(Long ossId) throws IOException;
+
+ Boolean deleteWithValidByIds(Collection ids, Boolean isValid);
}
diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysLogininforServiceImpl.java
index 82bc36f1..96fde0cf 100644
--- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysLogininforServiceImpl.java
+++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysLogininforServiceImpl.java
@@ -48,6 +48,7 @@ public class SysLogininforServiceImpl implements ISysLogininforService {
*/
@Async
@EventListener
+ @Override
public void recordLogininfor(LogininforEvent logininforEvent) {
HttpServletRequest request = logininforEvent.getRequest();
final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOperLogServiceImpl.java
index 668af836..a4192d31 100644
--- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOperLogServiceImpl.java
+++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOperLogServiceImpl.java
@@ -42,6 +42,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
*/
@Async
@EventListener
+ @Override
public void recordOper(OperLogEvent operLogEvent) {
SysOperLogBo operLog = MapstructUtils.convert(operLogEvent, SysOperLogBo.class);
// 远程查询操作地点
diff --git a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOssServiceImpl.java
index f997f5f9..4d14a75b 100644
--- a/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOssServiceImpl.java
+++ b/ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOssServiceImpl.java
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
+import java.util.Base64;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.core.constant.CacheNames;
import org.ruoyi.common.core.exception.ServiceException;
@@ -29,6 +30,7 @@ import org.ruoyi.system.mapper.SysOssMapper;
import org.ruoyi.system.service.ISysOssService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
@@ -48,127 +50,167 @@ import java.util.Map;
@Service
public class SysOssServiceImpl implements ISysOssService, OssService {
- private final SysOssMapper baseMapper;
+ private final SysOssMapper baseMapper;
- @Override
- public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) {
- LambdaQueryWrapper lqw = buildQueryWrapper(bo);
- Page result = baseMapper.selectVoPage(pageQuery.build(), lqw);
- List filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl);
- result.setRecords(filterResult);
- return TableDataInfo.build(result);
+ @Override
+ public TableDataInfo queryPageList(SysOssBo bo, PageQuery pageQuery) {
+ LambdaQueryWrapper lqw = buildQueryWrapper(bo);
+ Page result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+ List filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl);
+ result.setRecords(filterResult);
+ return TableDataInfo.build(result);
+ }
+
+ @Override
+ public List listByIds(Collection ossIds) {
+ List list = new ArrayList<>();
+ for (Long id : ossIds) {
+ SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
+ if (ObjectUtil.isNotNull(vo)) {
+ list.add(this.matchingUrl(vo));
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public String selectUrlByIds(String ossIds) {
+ List list = new ArrayList<>();
+ for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
+ SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
+ if (ObjectUtil.isNotNull(vo)) {
+ list.add(this.matchingUrl(vo).getUrl());
+ }
+ }
+ return String.join(StringUtils.SEPARATOR, list);
+ }
+
+ private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) {
+ Map params = bo.getParams();
+ LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
+ lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());
+ lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName,
+ bo.getOriginalName());
+ lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());
+ lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());
+ lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
+ SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
+ lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());
+ lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService());
+ return lqw;
+ }
+
+ @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId")
+ @Override
+ public SysOssVo getById(Long ossId) {
+ return baseMapper.selectVoById(ossId);
+ }
+
+ @Override
+ public void download(Long ossId, HttpServletResponse response) throws IOException {
+ SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
+ if (ObjectUtil.isNull(sysOss)) {
+ throw new ServiceException("文件数据不存在!");
+ }
+ FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
+ response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
+ OssClient storage = OssFactory.instance();
+ try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
+ int available = inputStream.available();
+ IoUtil.copy(inputStream, response.getOutputStream(), available);
+ response.setContentLength(available);
+ } catch (Exception e) {
+ throw new ServiceException(e.getMessage());
+ }
+ }
+ @Override
+ public String downloadByByte(Long ossId) throws IOException {
+ SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
+ if (ObjectUtil.isNull(sysOss)) {
+ throw new ServiceException("文件数据不存在!");
}
- @Override
- public List listByIds(Collection ossIds) {
- List list = new ArrayList<>();
- for (Long id : ossIds) {
- SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
- if (ObjectUtil.isNotNull(vo)) {
- list.add(this.matchingUrl(vo));
- }
- }
- return list;
+ OssClient storage = OssFactory.instance();
+ try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
+ // 读取输入流中的所有字节
+ byte[] bytes = IoUtil.readBytes(inputStream);
+ // 将字节数组转换为Base64编码的字符串
+ return Base64.getEncoder().encodeToString(bytes);
+ } catch (Exception e) {
+ throw new ServiceException(e.getMessage());
+ }
+ }
+
+ @Override
+ public MultipartFile downloadByFile(Long ossId) throws IOException {
+ SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
+ if (ObjectUtil.isNull(sysOss)) {
+ throw new ServiceException("文件数据不存在!");
}
- @Override
- public String selectUrlByIds(String ossIds) {
- List list = new ArrayList<>();
- for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) {
- SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
- if (ObjectUtil.isNotNull(vo)) {
- list.add(this.matchingUrl(vo).getUrl());
- }
- }
- return String.join(StringUtils.SEPARATOR, list);
+ OssClient storage = OssFactory.instance();
+ try (InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
+ byte[] content = IoUtil.readBytes(inputStream);
+ return new MockMultipartFile(
+ sysOss.getFileName(),
+ sysOss.getOriginalName(),
+ MediaType.APPLICATION_OCTET_STREAM_VALUE,
+ content
+ );
+ } catch (Exception e) {
+ throw new ServiceException(e.getMessage());
}
+ }
- private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) {
- Map params = bo.getParams();
- LambdaQueryWrapper lqw = Wrappers.lambdaQuery();
- lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName());
- lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName());
- lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix());
- lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl());
- lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
- SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
- lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy());
- lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService());
- return lqw;
+ @Override
+ public SysOssVo upload(MultipartFile file) {
+ String originalfileName = file.getOriginalFilename();
+ String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."),
+ originalfileName.length());
+ OssClient storage = OssFactory.instance();
+ UploadResult uploadResult;
+ try {
+ uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
+ } catch (IOException e) {
+ throw new ServiceException(e.getMessage());
}
+ // 保存文件信息
+ SysOss oss = new SysOss();
+ oss.setUrl(uploadResult.getUrl());
+ oss.setFileSuffix(suffix);
+ oss.setFileName(uploadResult.getFilename());
+ oss.setOriginalName(originalfileName);
+ oss.setService(storage.getConfigKey());
+ baseMapper.insert(oss);
+ SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
+ return this.matchingUrl(sysOssVo);
+ }
- @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId")
- @Override
- public SysOssVo getById(Long ossId) {
- return baseMapper.selectVoById(ossId);
+ @Override
+ public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
+ if (isValid) {
+ // 做一些业务上的校验,判断是否需要校验
}
+ List list = baseMapper.selectBatchIds(ids);
+ for (SysOss sysOss : list) {
+ OssClient storage = OssFactory.instance(sysOss.getService());
+ storage.delete(sysOss.getUrl());
+ }
+ return baseMapper.deleteBatchIds(ids) > 0;
+ }
- @Override
- public void download(Long ossId, HttpServletResponse response) throws IOException {
- SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
- if (ObjectUtil.isNull(sysOss)) {
- throw new ServiceException("文件数据不存在!");
- }
- FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
- response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
- OssClient storage = OssFactory.instance();
- try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
- int available = inputStream.available();
- IoUtil.copy(inputStream, response.getOutputStream(), available);
- response.setContentLength(available);
- } catch (Exception e) {
- throw new ServiceException(e.getMessage());
- }
- }
-
- @Override
- public SysOssVo upload(MultipartFile file) {
- String originalfileName = file.getOriginalFilename();
- String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
- OssClient storage = OssFactory.instance();
- UploadResult uploadResult;
- try {
- uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
- } catch (IOException e) {
- throw new ServiceException(e.getMessage());
- }
- // 保存文件信息
- SysOss oss = new SysOss();
- oss.setUrl(uploadResult.getUrl());
- oss.setFileSuffix(suffix);
- oss.setFileName(uploadResult.getFilename());
- oss.setOriginalName(originalfileName);
- oss.setService(storage.getConfigKey());
- baseMapper.insert(oss);
- SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
- return this.matchingUrl(sysOssVo);
- }
-
- @Override
- public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
- if (isValid) {
- // 做一些业务上的校验,判断是否需要校验
- }
- List list = baseMapper.selectBatchIds(ids);
- for (SysOss sysOss : list) {
- OssClient storage = OssFactory.instance(sysOss.getService());
- storage.delete(sysOss.getUrl());
- }
- return baseMapper.deleteBatchIds(ids) > 0;
- }
-
- /**
- * 匹配Url
- *
- * @param oss OSS对象
- * @return oss 匹配Url的OSS对象
- */
- private SysOssVo matchingUrl(SysOssVo oss) {
- OssClient storage = OssFactory.instance(oss.getService());
- // 仅修改桶类型为 private 的URL,临时URL时长为120s
- if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
- oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
- }
- return oss;
+ /**
+ * 匹配Url
+ *
+ * @param oss OSS对象
+ * @return oss 匹配Url的OSS对象
+ */
+ private SysOssVo matchingUrl(SysOssVo oss) {
+ OssClient storage = OssFactory.instance(oss.getService());
+ // 仅修改桶类型为 private 的URL,临时URL时长为120s
+ if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
+ oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
}
+ return oss;
+ }
}
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
index 4b57e25a..82362538 100644
--- 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
@@ -52,7 +52,7 @@ public class KnowledgeController extends BaseController {
private final IKnowledgeFragmentService fragmentService;
- private final PdfImageExtractService pdfImageExtractService;
+// private final PdfImageExtractService pdfImageExtractService;
/**
* 根据用户信息查询本地知识库
@@ -170,11 +170,11 @@ public class KnowledgeController extends BaseController {
* @param file PDF文件
* @return 文件名称和图片内容
*/
- @PostMapping("/extract-images")
- @Operation(summary = "提取PDF中的图片并调用大模型,识别图片内容并返回", description = "提取PDF中的图片并调用gpt-4o-mini,识别图片内容并返回")
- public R> extractImages(
- @RequestPart("file") MultipartFile file
- ) throws IOException {
- return R.ok(pdfImageExtractService.extractImages(file));
- }
+// @PostMapping("/extract-images")
+// @Operation(summary = "提取PDF中的图片并调用大模型,识别图片内容并返回", description = "提取PDF中的图片并调用gpt-4o-mini,识别图片内容并返回")
+// public R> extractImages(
+// @RequestPart("file") MultipartFile file
+// ) throws IOException {
+// return R.ok(pdfImageExtractService.extractImages(file));
+// }
}
diff --git a/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/DealFileService.java b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/DealFileService.java
new file mode 100644
index 00000000..882fce3a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/DealFileService.java
@@ -0,0 +1,390 @@
+package org.ruoyi.chat.service.knowledge;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
+import org.ruoyi.chain.loader.ResourceLoaderFactory;
+import org.ruoyi.constant.DealStatus;
+import org.ruoyi.domain.KnowledgeAttach;
+import org.ruoyi.domain.KnowledgeAttachPic;
+import org.ruoyi.domain.KnowledgeFragment;
+import org.ruoyi.domain.KnowledgeInfo;
+import org.ruoyi.domain.PdfFileContentResult;
+import org.ruoyi.domain.bo.StoreEmbeddingBo;
+import org.ruoyi.domain.vo.ChatModelVo;
+import org.ruoyi.domain.vo.KnowledgeAttachVo;
+import org.ruoyi.domain.vo.KnowledgeInfoVo;
+import org.ruoyi.mapper.KnowledgeAttachMapper;
+import org.ruoyi.mapper.KnowledgeAttachPicMapper;
+import org.ruoyi.mapper.KnowledgeFragmentMapper;
+import org.ruoyi.mapper.KnowledgeInfoMapper;
+import org.ruoyi.service.IChatModelService;
+import org.ruoyi.service.VectorStoreService;
+import org.ruoyi.service.impl.PdfImageExtractServiceImpl;
+import org.ruoyi.system.domain.vo.SysOssVo;
+import org.ruoyi.system.service.ISysOssService;
+import org.ruoyi.utils.ZipUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @Description:
+ * @Date: 2025/5/15 下午4:29
+ */
+@Service
+@RequiredArgsConstructor
+public class DealFileService {
+ private static final Logger log = LoggerFactory.getLogger(DealFileService.class);
+
+ private final KnowledgeInfoMapper baseMapper;
+
+ private final VectorStoreService vectorStoreService;
+
+ private final ResourceLoaderFactory resourceLoaderFactory;
+
+ private final KnowledgeFragmentMapper fragmentMapper;
+
+ private final KnowledgeAttachMapper attachMapper;
+
+ private final IChatModelService chatModelService;
+
+ private final ISysOssService ossService;
+
+// private final PdfImageExtractService pdfImageExtractService;
+
+ private final KnowledgeAttachPicMapper picMapper;
+
+ @Value("${pdf.extract.service.url}")
+ private String serviceUrl;
+ @Value("${pdf.extract.ai-api.url}")
+ private String aiApiUrl;
+ @Value("${pdf.extract.ai-api.key}")
+ private String aiApiKey;
+
+
+ @Async
+ public void dealVectorStatus(KnowledgeAttach attachItem) throws Exception {
+ try {
+ //锁定数据 更改VectorStatus 到进行中
+ if (attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getId, attachItem.getId())
+ ) == 0) {
+ return;
+ }
+ List knowledgeFragments = fragmentMapper.selectList(
+ new LambdaQueryWrapper()
+ .eq(KnowledgeFragment::getKid, attachItem.getKid())
+ .eq(KnowledgeFragment::getDocId, attachItem.getDocId())
+ );
+ if (ObjectUtil.isEmpty(knowledgeFragments)) {
+ throw new Exception("文件段落为空");
+ }
+ List fids = knowledgeFragments.stream()
+ .map(KnowledgeFragment::getFid)
+ .collect(Collectors.toList());
+ if (ObjectUtil.isEmpty(fids)) {
+ throw new Exception("fids 为空");
+ }
+ List chunkList = knowledgeFragments.stream()
+ .map(KnowledgeFragment::getContent)
+ .collect(Collectors.toList());
+
+ if (ObjectUtil.isEmpty(chunkList)) {
+ throw new Exception("chunkList 为空");
+ }
+ // 通过kid查询知识库信息
+ KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.lambdaQuery()
+ .eq(KnowledgeInfo::getId, attachItem.getKid()));
+ // 通过向量模型查询模型信息
+ ChatModelVo chatModelVo = chatModelService.selectModelByName(
+ knowledgeInfoVo.getEmbeddingModelName());
+
+ StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo();
+ storeEmbeddingBo.setKid(attachItem.getKid());
+ storeEmbeddingBo.setDocId(attachItem.getDocId());
+ storeEmbeddingBo.setFids(fids);
+ storeEmbeddingBo.setChunkList(chunkList);
+ storeEmbeddingBo.setVectorModelName(knowledgeInfoVo.getVectorModelName());
+ storeEmbeddingBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName());
+ storeEmbeddingBo.setApiKey(chatModelVo.getApiKey());
+ storeEmbeddingBo.setBaseUrl(chatModelVo.getApiHost());
+ vectorStoreService.storeEmbeddings(storeEmbeddingBo);
+
+ //设置处理完成
+ attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttach::getId, attachItem.getId()));
+ } catch (Exception e) {
+ //设置处理失败
+ attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_40)
+ .set(KnowledgeAttach::getRemark, attachItem.getRemark() + e.getMessage())
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttach::getId, attachItem.getId()));
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Async
+ public void dealPicStatus(KnowledgeAttach attachItem) throws Exception {
+ try {
+ //锁定数据 更改picStatus 到进行中
+ if (attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getPicStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getId, attachItem.getId())
+ ) == 0) {
+ return;
+ }
+ //获取附件
+ if (ObjectUtil.isEmpty(attachItem.getOssId())) {
+ log.error("==========OssId 为空,attachItem={}", attachItem);
+ throw new Exception("OssId 为空");
+ }
+ //获取oss文件
+ MultipartFile multipartFile = ossService.downloadByFile(attachItem.getOssId());
+ //拆解出图片ZIP
+ PdfImageExtractServiceImpl pdfImageExtractService = new PdfImageExtractServiceImpl(serviceUrl,
+ aiApiUrl, aiApiKey);
+ byte[] pngs = pdfImageExtractService.extractImages(multipartFile, "png", true);
+ //解压zip,得到图片文件
+ MultipartFile[] multipartFiles = ZipUtils.unzipToMultipartFiles(pngs);
+ //上传文件到OSS,写入表
+ for (MultipartFile file : multipartFiles) {
+ //先查找是否有相同图片名称,先做删除
+ List knowledgeAttachPics = picMapper.selectList(
+ new LambdaQueryWrapper()
+ .eq(KnowledgeAttachPic::getKid, attachItem.getKid())
+ .eq(KnowledgeAttachPic::getAid, attachItem.getId())
+ .eq(KnowledgeAttachPic::getDocName, file.getOriginalFilename())
+ );
+ if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) {
+ Collection ossIds = knowledgeAttachPics.stream()
+ .map(KnowledgeAttachPic::getOssId)
+ .collect(Collectors.toList());
+ ossService.deleteWithValidByIds(ossIds, false);
+ List collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId)
+ .collect(Collectors.toList());
+ picMapper.deleteByIds(collect);
+ }
+
+ SysOssVo upload = ossService.upload(file);
+ KnowledgeAttachPic entity = new KnowledgeAttachPic();
+ entity.setKid(attachItem.getKid());
+ entity.setAid(String.valueOf(attachItem.getId()));
+ entity.setDocName(file.getOriginalFilename());
+ entity.setDocType(
+ file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1));
+ entity.setOssId(upload.getOssId());
+ int[] ints = extractPageNumbers(file.getOriginalFilename());
+ if (ObjectUtil.isNotEmpty(ints)) {
+ assert ints != null;
+ if (ints.length == 2) {
+ entity.setPageNum(ints[0]);
+ entity.setIndexNum(ints[1]);
+ }
+ }
+ picMapper.insert(entity);
+ }
+
+ //设置处理完成
+ attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getId, attachItem.getId()));
+ } catch (Exception e) {
+ //设置处理失败
+ attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_40)
+ .set(KnowledgeAttach::getRemark, attachItem.getRemark() + e.getMessage())
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getId, attachItem.getId()));
+ throw new RuntimeException(e);
+ }
+
+ }
+
+
+ @Async
+ public void dealPicAnysStatus(KnowledgeAttachPic picItem) throws Exception {
+ try {
+ //锁定数据 更改 getPicAnysStatus 到进行中
+ if (picMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttachPic::getId, picItem.getId())
+ ) == 0) {
+ return;
+ }
+ SysOssVo ossVo = ossService.getById(picItem.getOssId());
+ if (ObjectUtil.isNotEmpty(ossVo)) {
+ String fileStr = ossService.downloadByByte(picItem.getOssId());
+ //调用第三方 分析图片内容
+ PdfImageExtractServiceImpl pdfImageExtractService = new PdfImageExtractServiceImpl(
+ serviceUrl,
+ aiApiUrl, aiApiKey);
+ List pdfFileContentResults = pdfImageExtractService.dealFileContent(
+ new String[]{fileStr});
+ if (ObjectUtil.isNotEmpty(pdfFileContentResults)) {
+ for (PdfFileContentResult resultItem : pdfFileContentResults) {
+ //图片解析内容回写到pic表
+ picMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttachPic::getContent, parseContent(resultItem.getContent()))
+ .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttachPic::getId, picItem.getId()));
+ //将图片解析内容 写入段落表 fragment
+ KnowledgeAttachVo knowledgeAttachVo = attachMapper.selectVoById(picItem.getAid());
+ if (ObjectUtil.isNotEmpty(knowledgeAttachVo)) {
+ String fid = RandomUtil.randomString(10);
+ KnowledgeFragment knowledgeFragment = new KnowledgeFragment();
+ knowledgeFragment.setKid(knowledgeAttachVo.getKid());
+ knowledgeFragment.setDocId(knowledgeAttachVo.getDocId());
+ knowledgeFragment.setFid(fid);
+ knowledgeFragment.setIdx(0);
+ knowledgeFragment.setContent(parseContent(resultItem.getContent()));
+ knowledgeFragment.setCreateTime(new Date());
+ fragmentMapper.insert(knowledgeFragment);
+
+ //更新attach表,需要所有图片都处理完毕
+ // 查询非30状态(完成状态)的记录数量
+ long nonStatus30Count = picMapper.selectCount(
+ new LambdaQueryWrapper()
+ .ne(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttachPic::getAid, picItem.getAid())
+ );
+ if (nonStatus30Count == 0) {
+ // 执行表更新操作
+ attachMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getId, picItem.getAid()));
+ }
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ //失败
+ picMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_40)
+ .set(KnowledgeAttachPic::getRemark, picItem.getRemark() + e.getMessage())
+ .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_20)
+ .eq(KnowledgeAttachPic::getId, picItem.getId()));
+ throw new RuntimeException(e);
+ } finally {
+ //将图片分析失败的数据 重新设置状态
+ picMapper.update(new LambdaUpdateWrapper()
+ .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_40));
+ }
+ }
+
+
+ /**
+ * 从文件名中提取page后面的两个数字
+ *
+ * @param fileName 文件名
+ * @return 包含两个数字的数组,如果未找到则返回null
+ */
+ public static int[] extractPageNumbers(String fileName) {
+ // 查找"page_"的位置
+ int pageIndex = fileName.indexOf("page_");
+
+ if (pageIndex == -1) {
+ return null;
+ }
+
+ // 从"page_"后开始截取
+ String afterPage = fileName.substring(pageIndex + 5);
+
+ // 按下划线分割
+ String[] parts = afterPage.split("_");
+
+ if (parts.length >= 2) {
+ try {
+ // 提取两个数字
+ int firstNumber = Integer.parseInt(parts[0]);
+
+ // 对于第二个数字,需要去掉可能的文件扩展名
+ String secondPart = parts[1];
+ int dotIndex = secondPart.indexOf(".");
+ if (dotIndex != -1) {
+ secondPart = secondPart.substring(0, dotIndex);
+ }
+
+ int secondNumber = Integer.parseInt(secondPart);
+
+ return new int[]{firstNumber, secondNumber};
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ return null;
+ }
+
+ public static String parseContent(String jsonString) {
+ try {
+ // 创建ObjectMapper实例
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ // 解析JSON字符串
+ JsonNode rootNode = objectMapper.readTree(jsonString);
+
+ // 获取choices数组的第一个元素
+ JsonNode choicesNode = rootNode.get("choices");
+ if (choicesNode != null && choicesNode.isArray() && choicesNode.size() > 0) {
+ // 获取第一个choice
+ JsonNode firstChoice = choicesNode.get(0);
+
+ // 获取message节点
+ JsonNode messageNode = firstChoice.get("message");
+ if (messageNode != null) {
+ // 获取content字段的值
+ JsonNode contentNode = messageNode.get("content");
+ if (contentNode != null) {
+ return contentNode.asText();
+ }
+ }
+ }
+
+ return "无法找到content内容";
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "解析JSON时发生错误: " + e.getMessage();
+ }
+ }
+
+
+}
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
index d94d3205..fb237b2d 100644
--- 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
@@ -1,44 +1,63 @@
package org.ruoyi.chat.service.knowledge;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
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.constant.DealStatus;
+import org.ruoyi.constant.FileType;
import org.ruoyi.core.page.PageQuery;
import org.ruoyi.core.page.TableDataInfo;
import org.ruoyi.domain.ChatModel;
import org.ruoyi.domain.KnowledgeAttach;
+import org.ruoyi.domain.KnowledgeAttachPic;
import org.ruoyi.domain.KnowledgeFragment;
import org.ruoyi.domain.KnowledgeInfo;
+import org.ruoyi.domain.PdfFileContentResult;
import org.ruoyi.domain.bo.KnowledgeInfoBo;
import org.ruoyi.domain.bo.KnowledgeInfoUploadBo;
import org.ruoyi.domain.bo.StoreEmbeddingBo;
import org.ruoyi.domain.vo.ChatModelVo;
+import org.ruoyi.domain.vo.KnowledgeAttachVo;
import org.ruoyi.domain.vo.KnowledgeInfoVo;
import org.ruoyi.mapper.KnowledgeAttachMapper;
+import org.ruoyi.mapper.KnowledgeAttachPicMapper;
import org.ruoyi.mapper.KnowledgeFragmentMapper;
import org.ruoyi.mapper.KnowledgeInfoMapper;
import org.ruoyi.service.IChatModelService;
+import org.ruoyi.service.PdfImageExtractService;
import org.ruoyi.service.VectorStoreService;
import org.ruoyi.service.IKnowledgeInfoService;
+import org.ruoyi.service.impl.PdfImageExtractServiceImpl;
+import org.ruoyi.system.domain.vo.SysOssVo;
+import org.ruoyi.utils.ZipUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
+import org.ruoyi.system.service.ISysOssService;
import java.io.IOException;
import java.util.*;
+
/**
* 知识库Service业务层处理
*
@@ -49,216 +68,330 @@ import java.util.*;
@Service
public class KnowledgeInfoServiceImpl implements IKnowledgeInfoService {
- private static final Logger log = LoggerFactory.getLogger(KnowledgeInfoServiceImpl.class);
- private final KnowledgeInfoMapper baseMapper;
+ private static final Logger log = LoggerFactory.getLogger(KnowledgeInfoServiceImpl.class);
+ private final KnowledgeInfoMapper baseMapper;
- private final VectorStoreService vectorStoreService;
+ private final VectorStoreService vectorStoreService;
- private final ResourceLoaderFactory resourceLoaderFactory;
+ private final ResourceLoaderFactory resourceLoaderFactory;
- private final KnowledgeFragmentMapper fragmentMapper;
+ private final KnowledgeFragmentMapper fragmentMapper;
- private final KnowledgeAttachMapper attachMapper;
+ private final KnowledgeAttachMapper attachMapper;
- private final IChatModelService chatModelService;
+ private final IChatModelService chatModelService;
- /**
- * 查询知识库
- */
- @Override
- public KnowledgeInfoVo queryById(Long id){
- return baseMapper.selectVoById(id);
+ private final ISysOssService ossService;
+
+// private final PdfImageExtractService pdfImageExtractService;
+
+ private final KnowledgeAttachPicMapper picMapper;
+
+ private final DealFileService dealFileService;
+
+ @Value("${pdf.extract.service.url}")
+ private String serviceUrl;
+ @Value("${pdf.extract.ai-api.url}")
+ private String aiApiUrl;
+ @Value("${pdf.extract.ai-api.key}")
+ private String aiApiKey;
+
+ /**
+ * 查询知识库
+ */
+ @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());
+ 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);
+ if (knowledgeInfo != null) {
+ vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()),
+ bo.getVectorModelName());
+ }
+ } 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 -> {
+ vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId()),
+ knowledgeInfoVo.getVectorModelName());
+ });
+ // 删除附件和知识片段
+ fragmentMapper.deleteByMap(map);
+ List knowledgeAttachVos = attachMapper.selectVoByMap(map);
+ if (ObjectUtil.isNotEmpty(knowledgeAttachVos)) {
+ Collection ossIds = knowledgeAttachVos.stream()
+ .map(KnowledgeAttachVo::getOssId)
+ .collect(Collectors.toList());
+ //删除oss
+ ossService.deleteWithValidByIds(ossIds, false);
+
+ //删除图片oss
+ List knowledgeAttachPics = picMapper.selectList(
+ new LambdaQueryWrapper()
+ .in(KnowledgeAttachPic::getKid,
+ knowledgeAttachVos.stream().map(KnowledgeAttachVo::getKid)
+ .collect(Collectors.toList()))
+ .in(KnowledgeAttachPic::getAid,
+ knowledgeAttachVos.stream().map(KnowledgeAttachVo::getId)
+ .collect(Collectors.toList()))
+ );
+ if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) {
+ Collection tossIds = knowledgeAttachPics.stream()
+ .map(KnowledgeAttachPic::getOssId)
+ .collect(Collectors.toList());
+ ossService.deleteWithValidByIds(tossIds, false);
+ List collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId)
+ .collect(Collectors.toList());
+ picMapper.deleteByIds(collect);
+ }
+ }
+ attachMapper.deleteByMap(map);
+ // 删除知识库
+ baseMapper.deleteByMap(map);
+ }
+
+ @Override
+ public void upload(KnowledgeInfoUploadBo bo) {
+ storeContent(bo.getFile(), bo.getKid());
+ }
+
+ public void storeContent(MultipartFile file, String kid) {
+ if (file == null || file.isEmpty()) {
+ throw new IllegalArgumentException("File cannot be null or empty");
}
- /**
- * 查询知识库列表
- */
- @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());
- 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());
+ SysOssVo uploadDto = null;
+ 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)) {
+ // Upload file to OSS
+ uploadDto = ossService.upload(file);
+ for (int i = 0; i < chunkList.size(); i++) {
+ String fid = RandomUtil.randomString(10);
+ 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);
}
- return flag;
+ }
+ fragmentMapper.insertBatch(knowledgeFragmentList);
+ } catch (IOException e) {
+ log.error("保存知识库信息失败!{}", e.getMessage());
}
-
- /**
- * 修改知识库
- */
- @Override
- public Boolean updateByBo(KnowledgeInfoBo bo) {
- KnowledgeInfo update = MapstructUtils.convert(bo, KnowledgeInfo.class);
- validEntityBeforeSave(update);
- return baseMapper.updateById(update) > 0;
+ knowledgeAttach.setContent(content);
+ knowledgeAttach.setCreateTime(new Date());
+ if (ObjectUtil.isNotEmpty(uploadDto) && ObjectUtil.isNotEmpty(uploadDto.getOssId())) {
+ knowledgeAttach.setOssId(uploadDto.getOssId());
+ //只有pdf文件 才需要拆解图片和分析图片内容
+ if (FileType.PDF.equals(knowledgeAttach.getDocType())) {
+ knowledgeAttach.setPicStatus(DealStatus.STATUS_10);
+ knowledgeAttach.setPicAnysStatus(DealStatus.STATUS_10);
+ } else {
+ knowledgeAttach.setPicStatus(DealStatus.STATUS_30);
+ knowledgeAttach.setPicAnysStatus(DealStatus.STATUS_30);
+ }
+ //所有文件上传后,都需要同步到向量数据库
+ knowledgeAttach.setVectorStatus(DealStatus.STATUS_10);
}
+ attachMapper.insert(knowledgeAttach);
- /**
- * 保存前的数据校验
- */
- private void validEntityBeforeSave(KnowledgeInfo entity){
- //TODO 做一些数据校验,如唯一约束
+ }
+
+
+ /**
+ * 检查用户是否有删除知识库权限
+ *
+ * @param knowledgeInfoList 知识库列表
+ */
+ public void check(List knowledgeInfoList) {
+ LoginUser loginUser = LoginHelper.getLoginUser();
+ for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) {
+ if (!knowledgeInfoVo.getUid().equals(loginUser.getUserId())) {
+ throw new SecurityException("权限不足");
+ }
}
+ }
- /**
- * 批量删除知识库
- */
- @Override
- public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) {
- if(isValid){
- //TODO 做一些业务上的校验,判断是否需要校验
- }
- return baseMapper.deleteBatchIds(ids) > 0;
+
+ /**
+ * 第一步 定时 拆解PDF文件中的图片
+ */
+ @Scheduled(fixedDelay = 15000) // 每3秒执行一次
+ public void dealKnowledgeAttachPic() throws Exception {
+ //处理 拆解PDF文件中的图片的记录
+ List knowledgeAttaches = attachMapper.selectList(
+ new LambdaQueryWrapper()
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_10)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ );
+ log.info("===============拆解PDF文件中的图片 size = {}", knowledgeAttaches.size());
+ if (ObjectUtil.isNotEmpty(knowledgeAttaches)) {
+ for (KnowledgeAttach attachItem : knowledgeAttaches) {
+ dealFileService.dealPicStatus(attachItem);
+ }
}
+ }
- @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);
- if (knowledgeInfo != null) {
- vectorStoreService.createSchema(String.valueOf(knowledgeInfo.getId()),bo.getVectorModelName());
- }
- }else {
- baseMapper.updateById(knowledgeInfo);
- }
+
+ /**
+ * 第二步 定时 解析图片内容
+ */
+ @Scheduled(fixedDelay = 15000)
+ public void dealKnowledgeAttachPicAnys() throws Exception {
+ //获取未处理的图片记录
+ List knowledgeAttachPics = picMapper.selectList(
+ new LambdaQueryWrapper()
+ .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10)
+ );
+ if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) {
+ for (KnowledgeAttachPic picItem : knowledgeAttachPics) {
+ dealFileService.dealPicAnysStatus(picItem);
+ }
}
+ }
- @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 -> {
- vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId()),knowledgeInfoVo.getVectorModelName());
- });
- // 删除附件和知识片段
- 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(10);
- 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) {
- log.error("保存知识库信息失败!{}", e.getMessage());
- }
- knowledgeAttach.setContent(content);
- knowledgeAttach.setCreateTime(new Date());
- attachMapper.insert(knowledgeAttach);
-
- // 通过kid查询知识库信息
- KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.lambdaQuery()
- .eq(KnowledgeInfo::getId, kid));
-
- // 通过向量模型查询模型信息
- ChatModelVo chatModelVo = chatModelService.selectModelByName(knowledgeInfoVo.getEmbeddingModelName());
-
- StoreEmbeddingBo storeEmbeddingBo = new StoreEmbeddingBo();
- storeEmbeddingBo.setKid(kid);
- storeEmbeddingBo.setDocId(docId);
- storeEmbeddingBo.setFids(fids);
- storeEmbeddingBo.setChunkList(chunkList);
- storeEmbeddingBo.setVectorModelName(knowledgeInfoVo.getVectorModelName());
- storeEmbeddingBo.setEmbeddingModelName(knowledgeInfoVo.getEmbeddingModelName());
- storeEmbeddingBo.setApiKey(chatModelVo.getApiKey());
- storeEmbeddingBo.setBaseUrl(chatModelVo.getApiHost());
- vectorStoreService.storeEmbeddings(storeEmbeddingBo);
+
+ /**
+ * 第三步 定时 处理 附件上传后上传向量数据库
+ */
+ @Scheduled(fixedDelay = 30000) // 每3秒执行一次
+ public void dealKnowledgeAttachVector() throws Exception {
+ //处理 需要上传向量数据库的记录
+ List knowledgeAttaches = attachMapper.selectList(
+ new LambdaQueryWrapper()
+ .eq(KnowledgeAttach::getPicStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getPicAnysStatus, DealStatus.STATUS_30)
+ .eq(KnowledgeAttach::getVectorStatus, DealStatus.STATUS_10)
+ );
+ log.info("===============上传向量数据库 size = {}", knowledgeAttaches.size());
+ if (ObjectUtil.isNotEmpty(knowledgeAttaches)) {
+ for (KnowledgeAttach attachItem : knowledgeAttaches) {
+ dealFileService.dealVectorStatus(attachItem);
+ }
}
+ }
- /**
- * 检查用户是否有删除知识库权限
- *
- * @param knowledgeInfoList 知识库列表
- */
- public void check(List knowledgeInfoList){
- LoginUser loginUser = LoginHelper.getLoginUser();
- for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) {
- if(!knowledgeInfoVo.getUid().equals(loginUser.getUserId())){
- throw new SecurityException("权限不足");
- }
- }
- }
}
diff --git a/script/sql/update/202505141010.sql b/script/sql/update/202505141010.sql
index 2cb86be4..e1b81b25 100644
--- a/script/sql/update/202505141010.sql
+++ b/script/sql/update/202505141010.sql
@@ -1,6 +1,10 @@
ALTER TABLE `knowledge_attach`
+ADD COLUMN `oss_id` bigint(20) NOT NULL COMMENT '对象存储主键' AFTER `remark`,
ADD COLUMN `pic_status` tinyint(1) NOT NULL DEFAULT 10 COMMENT '拆解图片状态10未开始,20进行中,30已完成' AFTER `oss_id`,
ADD COLUMN `pic_anys_status` tinyint(1) NOT NULL DEFAULT 10 COMMENT '分析图片状态10未开始,20进行中,30已完成' AFTER `pic_status`,
ADD COLUMN `vector_status` tinyint(1) NOT NULL DEFAULT 10 COMMENT '写入向量数据库状态10未开始,20进行中,30已完成' AFTER `pic_anys_status`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`) USING BTREE;
+
+ALTER TABLE `knowledge_attach`
+MODIFY COLUMN `remark` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '备注' AFTER `update_time`;