From c6ffbcb3cf9b014c7b99170e5dd1c97317e9c4a5 Mon Sep 17 00:00:00 2001
From: zhouweiyi
Date: 星期四, 15 五月 2025 17:46:38 +0800
Subject: [PATCH] feat:pdf文件解析图片和分析图片,上传向量数据库都修改成 成异步处理

---
 script/sql/update/202505141010.sql                                                                            |    4 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/mapper/KnowledgeAttachPicMapper.java            |   15 
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/DealFileService.java                  |  390 +++++++++++
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/IKnowledgeAttachPicService.java         |   47 +
 ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOperLogService.java             |    2 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java                             |   88 ++
 ruoyi-modules-api/ruoyi-system-api/pom.xml                                                                    |    6 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachServiceImpl.java    |  212 +++--
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/bo/KnowledgeAttachPicBo.java             |   90 ++
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/constant/DealStatus.java                        |    2 
 ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysLogininforService.java          |    3 
 ruoyi-modules-api/ruoyi-knowledge-api/pom.xml                                                                 |    4 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java        |    2 
 ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java                                                   |    2 
 ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOperLogServiceImpl.java     |    1 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/KnowledgeAttachPic.java                  |   81 ++
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/KnowledgeAttachPicServiceImpl.java |  123 +++
 ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/ISysOssService.java                 |   16 
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java         |  513 +++++++++-----
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java    |   25 
 ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysLogininforServiceImpl.java  |    1 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/vo/KnowledgeAttachPicVo.java             |   92 ++
 ruoyi-modules-api/ruoyi-system-api/src/main/java/org/ruoyi/system/service/impl/SysOssServiceImpl.java         |  264 ++++---
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java           |   16 
 24 files changed, 1,585 insertions(+), 414 deletions(-)

diff --git a/ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java b/ruoyi-admin/src/main/java/org/ruoyi/RuoYiAIApplication.java
index df57f7e..cc65b7b 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 @@
 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 @@
  */
 @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 83eac39..4b4c1fe 100644
--- a/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
@@ -114,6 +114,10 @@
             <artifactId>commons-io</artifactId>
             <version>2.17.0</version>
         </dependency>
+      <dependency>
+        <groupId>org.ruoyi</groupId>
+        <artifactId>ruoyi-system-api</artifactId>
+      </dependency>
 
     </dependencies>
 
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 aadf19a..273b1b0 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 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 0000000..f0a28c7
--- /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;
+
+  /**
+   * 鐭ヨ瘑搴搃d
+   */
+  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 0000000..6fe9e3c
--- /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;
+
+  /**
+   * 鐭ヨ瘑搴搃d
+   */
+  @NotBlank(message = "鐭ヨ瘑搴搃d涓嶈兘涓虹┖", 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 0000000..8972d80
--- /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;
+
+  /**
+   * 鐭ヨ瘑搴搃d
+   */
+  @ExcelProperty(value = "鐭ヨ瘑搴搃d")
+  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 0000000..b1db81c
--- /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<KnowledgeAttachPic, KnowledgeAttachPicVo> {
+
+}
\ 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 0000000..7cdc1eb
--- /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<KnowledgeAttachPicVo> queryPageList(KnowledgeAttachPicBo bo, PageQuery pageQuery);
+
+  /**
+   * 鏌ヨ鐭ヨ瘑搴撻檮浠跺浘鐗囧垪鍒楄〃
+   */
+  List<KnowledgeAttachPicVo> queryList(KnowledgeAttachPicBo bo);
+
+  /**
+   * 鏂板鐭ヨ瘑搴撻檮浠跺浘鐗囧垪
+   */
+  Boolean insertByBo(KnowledgeAttachPicBo bo);
+
+  /**
+   * 淇敼鐭ヨ瘑搴撻檮浠跺浘鐗囧垪
+   */
+  Boolean updateByBo(KnowledgeAttachPicBo bo);
+
+  /**
+   * 鏍¢獙骞舵壒閲忓垹闄ょ煡璇嗗簱闄勪欢鍥剧墖鍒椾俊鎭�
+   */
+  Boolean deleteWithValidByIds(Collection<Long> 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 0000000..4b088e4
--- /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<KnowledgeAttachPicVo> queryPageList(KnowledgeAttachPicBo bo,
+      PageQuery pageQuery) {
+    LambdaQueryWrapper<KnowledgeAttachPic> lqw = buildQueryWrapper(bo);
+    Page<KnowledgeAttachPicVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+    return TableDataInfo.build(result);
+  }
+
+  /**
+   * 鏌ヨ鐭ヨ瘑搴撻檮浠跺浘鐗囧垪鍒楄〃
+   */
+  @Override
+  public List<KnowledgeAttachPicVo> queryList(KnowledgeAttachPicBo bo) {
+    LambdaQueryWrapper<KnowledgeAttachPic> lqw = buildQueryWrapper(bo);
+    return baseMapper.selectVoList(lqw);
+  }
+
+  private LambdaQueryWrapper<KnowledgeAttachPic> buildQueryWrapper(KnowledgeAttachPicBo bo) {
+    Map<String, Object> params = bo.getParams();
+    LambdaQueryWrapper<KnowledgeAttachPic> 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<Long> 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 46869cb..9bd2c45 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.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 @@
 @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<KnowledgeAttachVo> queryPageList(KnowledgeAttachBo bo, PageQuery pageQuery) {
+    LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo);
+    Page<KnowledgeAttachVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+    return TableDataInfo.build(result);
+  }
+
+  /**
+   * 鏌ヨ鐭ヨ瘑搴撻檮浠跺垪琛�
+   */
+  @Override
+  public List<KnowledgeAttachVo> queryList(KnowledgeAttachBo bo) {
+    LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo);
+    return baseMapper.selectVoList(lqw);
+  }
+
+  private LambdaQueryWrapper<KnowledgeAttach> buildQueryWrapper(KnowledgeAttachBo bo) {
+    Map<String, Object> params = bo.getParams();
+    LambdaQueryWrapper<KnowledgeAttach> 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<KnowledgeAttachVo> queryPageList(KnowledgeAttachBo bo, PageQuery pageQuery) {
-        LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo);
-        Page<KnowledgeAttachVo> 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<Long> ids, Boolean isValid) {
+    if (isValid) {
+      //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
     }
+    return baseMapper.deleteBatchIds(ids) > 0;
+  }
 
-    /**
-     * 鏌ヨ鐭ヨ瘑搴撻檮浠跺垪琛�
-     */
-    @Override
-    public List<KnowledgeAttachVo> queryList(KnowledgeAttachBo bo) {
-        LambdaQueryWrapper<KnowledgeAttach> lqw = buildQueryWrapper(bo);
-        return baseMapper.selectVoList(lqw);
+  @Override
+  public void removeKnowledgeAttach(String docId) {
+    Map<String, Object> map = new HashMap<>();
+    map.put("doc_id", docId);
+    List<KnowledgeAttachVo> knowledgeAttachVos = baseMapper.selectVoByMap(map);
+    if (ObjectUtil.isNotEmpty(knowledgeAttachVos)) {
+      Collection<Long> ossIds = knowledgeAttachVos.stream()
+          .map(KnowledgeAttachVo::getOssId)
+          .collect(Collectors.toList());
+      //鍒犻櫎oss
+      ossService.deleteWithValidByIds(ossIds, false);
+      //鍒犻櫎鍥剧墖oss
+      List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
+          new LambdaQueryWrapper<KnowledgeAttachPic>()
+              .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<Long> tossIds = knowledgeAttachPics.stream()
+            .map(KnowledgeAttachPic::getOssId)
+            .collect(Collectors.toList());
+        ossService.deleteWithValidByIds(tossIds, false);
+        List<Long> collect = knowledgeAttachPics.stream().map(KnowledgeAttachPic::getId)
+            .collect(Collectors.toList());
+        picMapper.deleteByIds(collect);
+      }
     }
+    baseMapper.deleteByMap(map);
+    fragmentMapper.deleteByMap(map);
+  }
 
-    private LambdaQueryWrapper<KnowledgeAttach> buildQueryWrapper(KnowledgeAttachBo bo) {
-        Map<String, Object> params = bo.getParams();
-        LambdaQueryWrapper<KnowledgeAttach> 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<Long> ids, Boolean isValid) {
-        if(isValid){
-            //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
-        }
-        return baseMapper.deleteBatchIds(ids) > 0;
-    }
-
-    @Override
-    public void removeKnowledgeAttach(String docId) {
-        Map<String,Object> 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 @@
             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 55ce87e..29aae8e 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.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 @@
 /**
  * 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 @@
 
   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 @@
    * @return 鏂囦欢鍐呭缁撴灉鍒楄〃
    * @throws IOException 濡傛灉API璋冪敤杩囩▼涓彂鐢熼敊璇�
    */
-  @Override
+//  @Override
   public List<PdfFileContentResult> dealFileContent(String[] unzip) throws IOException {
     List<PdfFileContentResult> results = new ArrayList<>();
     int i = 0;
@@ -110,6 +115,7 @@
       // 鎵ц璇锋眰
       try {
         log.info("=============call=" + ++i);
+
         Response response = client.newCall(request).execute();
         log.info("=============response=" + response);
         if (!response.isSuccessful()) {
@@ -126,11 +132,10 @@
         throw new RuntimeException(e);
       }
     }
-
     return results;
   }
 
-  @Override
+//  @Override
   public List<PdfFileContentResult> 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 b5f7962..7e1f6f9 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 @@
         for (int i = 0; i < chunkList.size(); i++) {
             Map<String, Object> 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 7ae1b2b..3c16131 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.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 @@
         }
         return base64Contents.toArray(new String[0]);
     }
+
+  /**
+   * 瑙e帇ZIP鏂囦欢骞惰繑鍥濵ultipartFile鏁扮粍
+   *
+   * @param zipData ZIP鏂囦欢鐨勫瓧鑺傛暟缁�
+   * @return MultipartFile鏁扮粍
+   * @throws IOException 濡傛灉瑙e帇杩囩▼涓彂鐢熼敊璇�
+   */
+  public static MultipartFile[] unzipToMultipartFiles(byte[] zipData) throws IOException {
+    List<MultipartFile> 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 69fe0cc..1f3deac 100644
--- a/ruoyi-modules-api/ruoyi-system-api/pom.xml
+++ b/ruoyi-modules-api/ruoyi-system-api/pom.xml
@@ -17,5 +17,11 @@
         <maven.compiler.target>17</maven.compiler.target>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     </properties>
+  <dependencies>
+    <dependency>
+      <groupId>org.springframework</groupId>
+      <artifactId>spring-test</artifactId>
+    </dependency>
+  </dependencies>
 
 </project>
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 2b2d055..8f3f50c 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 @@
      * 娓呯┖绯荤粺鐧诲綍鏃ュ織
      */
     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 65fae97..e3a39b8 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 @@
      * 娓呯┖鎿嶄綔鏃ュ織
      */
     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 1461f0c..a9ff495 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 @@
  */
 public interface ISysOssService {
 
-    TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery);
+  TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery);
 
-    List<SysOssVo> listByIds(Collection<Long> ossIds);
+  List<SysOssVo> listByIds(Collection<Long> 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<Long> ids, Boolean isValid);
+  MultipartFile downloadByFile(Long ossId) throws IOException;
+
+  String downloadByByte(Long ossId) throws IOException;
+
+  Boolean deleteWithValidByIds(Collection<Long> 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 82bc36f..96fde0c 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 @@
      */
     @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 668af83..a4192d3 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 @@
      */
     @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 f997f5f..4d14a75 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.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.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 @@
 @Service
 public class SysOssServiceImpl implements ISysOssService, OssService {
 
-    private final SysOssMapper baseMapper;
+  private final SysOssMapper baseMapper;
 
-    @Override
-    public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
-        LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
-        Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
-        List<SysOssVo> filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl);
-        result.setRecords(filterResult);
-        return TableDataInfo.build(result);
+  @Override
+  public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
+    LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
+    Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+    List<SysOssVo> filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl);
+    result.setRecords(filterResult);
+    return TableDataInfo.build(result);
+  }
+
+  @Override
+  public List<SysOssVo> listByIds(Collection<Long> ossIds) {
+    List<SysOssVo> 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<String> 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<SysOss> buildQueryWrapper(SysOssBo bo) {
+    Map<String, Object> params = bo.getParams();
+    LambdaQueryWrapper<SysOss> 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<SysOssVo> listByIds(Collection<Long> ossIds) {
-        List<SysOssVo> 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<String> 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<SysOss> buildQueryWrapper(SysOssBo bo) {
-        Map<String, Object> params = bo.getParams();
-        LambdaQueryWrapper<SysOss> 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<Long> ids, Boolean isValid) {
+    if (isValid) {
+      // 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
     }
+    List<SysOss> 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());
-        }
+  /**
+   * 鍖归厤Url
+   *
+   * @param oss OSS瀵硅薄
+   * @return oss 鍖归厤Url鐨凮SS瀵硅薄
+   */
+  private SysOssVo matchingUrl(SysOssVo oss) {
+    OssClient storage = OssFactory.instance(oss.getService());
+    // 浠呬慨鏀规《绫诲瀷涓� private 鐨刄RL锛屼复鏃禪RL鏃堕暱涓�120s
+    if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
+      oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
     }
-
-    @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<Long> ids, Boolean isValid) {
-        if (isValid) {
-            // 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
-        }
-        List<SysOss> 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鐨凮SS瀵硅薄
-     */
-    private SysOssVo matchingUrl(SysOssVo oss) {
-        OssClient storage = OssFactory.instance(oss.getService());
-        // 浠呬慨鏀规《绫诲瀷涓� private 鐨刄RL锛屼复鏃禪RL鏃堕暱涓�120s
-        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
-            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
-        }
-        return oss;
-    }
+    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 4b57e25..8236253 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 @@
 
   private final IKnowledgeFragmentService fragmentService;
 
-  private final PdfImageExtractService pdfImageExtractService;
+//  private final PdfImageExtractService pdfImageExtractService;
 
   /**
    * 鏍规嵁鐢ㄦ埛淇℃伅鏌ヨ鏈湴鐭ヨ瘑搴�
@@ -170,11 +170,11 @@
    * @param file PDF鏂囦欢
    * @return 鏂囦欢鍚嶇О鍜屽浘鐗囧唴瀹�
    */
-  @PostMapping("/extract-images")
-  @Operation(summary = "鎻愬彇PDF涓殑鍥剧墖骞惰皟鐢ㄥぇ妯″瀷,璇嗗埆鍥剧墖鍐呭骞惰繑鍥�", description = "鎻愬彇PDF涓殑鍥剧墖骞惰皟鐢╣pt-4o-mini,璇嗗埆鍥剧墖鍐呭骞惰繑鍥�")
-  public R<List<PdfFileContentResult>> extractImages(
-      @RequestPart("file") MultipartFile file
-  ) throws IOException {
-    return R.ok(pdfImageExtractService.extractImages(file));
-  }
+//  @PostMapping("/extract-images")
+//  @Operation(summary = "鎻愬彇PDF涓殑鍥剧墖骞惰皟鐢ㄥぇ妯″瀷,璇嗗埆鍥剧墖鍐呭骞惰繑鍥�", description = "鎻愬彇PDF涓殑鍥剧墖骞惰皟鐢╣pt-4o-mini,璇嗗埆鍥剧墖鍐呭骞惰繑鍥�")
+//  public R<List<PdfFileContentResult>> 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 0000000..882fce3
--- /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<KnowledgeAttach>()
+          .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<KnowledgeFragment> knowledgeFragments = fragmentMapper.selectList(
+          new LambdaQueryWrapper<KnowledgeFragment>()
+              .eq(KnowledgeFragment::getKid, attachItem.getKid())
+              .eq(KnowledgeFragment::getDocId, attachItem.getDocId())
+      );
+      if (ObjectUtil.isEmpty(knowledgeFragments)) {
+        throw new Exception("鏂囦欢娈佃惤涓虹┖");
+      }
+      List<String> fids = knowledgeFragments.stream()
+          .map(KnowledgeFragment::getFid)
+          .collect(Collectors.toList());
+      if (ObjectUtil.isEmpty(fids)) {
+        throw new Exception("fids 涓虹┖");
+      }
+      List<String> chunkList = knowledgeFragments.stream()
+          .map(KnowledgeFragment::getContent)
+          .collect(Collectors.toList());
+
+      if (ObjectUtil.isEmpty(chunkList)) {
+        throw new Exception("chunkList 涓虹┖");
+      }
+      // 閫氳繃kid鏌ヨ鐭ヨ瘑搴撲俊鎭�
+      KnowledgeInfoVo knowledgeInfoVo = baseMapper.selectVoOne(Wrappers.<KnowledgeInfo>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<KnowledgeAttach>()
+          .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<KnowledgeAttach>()
+          .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<KnowledgeAttach>()
+          .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 涓虹┖锛宎ttachItem={}", attachItem);
+        throw new Exception("OssId 涓虹┖");
+      }
+      //鑾峰彇oss鏂囦欢
+      MultipartFile multipartFile = ossService.downloadByFile(attachItem.getOssId());
+      //鎷嗚В鍑哄浘鐗嘮IP
+      PdfImageExtractServiceImpl pdfImageExtractService = new PdfImageExtractServiceImpl(serviceUrl,
+          aiApiUrl, aiApiKey);
+      byte[] pngs = pdfImageExtractService.extractImages(multipartFile, "png", true);
+      //瑙e帇zip锛屽緱鍒板浘鐗囨枃浠�
+      MultipartFile[] multipartFiles = ZipUtils.unzipToMultipartFiles(pngs);
+      //涓婁紶鏂囦欢鍒癘SS锛屽啓鍏ヨ〃
+      for (MultipartFile file : multipartFiles) {
+        //鍏堟煡鎵炬槸鍚︽湁鐩稿悓鍥剧墖鍚嶇О锛屽厛鍋氬垹闄�
+        List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
+            new LambdaQueryWrapper<KnowledgeAttachPic>()
+                .eq(KnowledgeAttachPic::getKid, attachItem.getKid())
+                .eq(KnowledgeAttachPic::getAid, attachItem.getId())
+                .eq(KnowledgeAttachPic::getDocName, file.getOriginalFilename())
+        );
+        if (ObjectUtil.isNotEmpty(knowledgeAttachPics)) {
+          Collection<Long> ossIds = knowledgeAttachPics.stream()
+              .map(KnowledgeAttachPic::getOssId)
+              .collect(Collectors.toList());
+          ossService.deleteWithValidByIds(ossIds, false);
+          List<Long> 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<KnowledgeAttach>()
+          .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<KnowledgeAttach>()
+          .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<KnowledgeAttachPic>()
+          .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<PdfFileContentResult> pdfFileContentResults = pdfImageExtractService.dealFileContent(
+            new String[]{fileStr});
+        if (ObjectUtil.isNotEmpty(pdfFileContentResults)) {
+          for (PdfFileContentResult resultItem : pdfFileContentResults) {
+            //鍥剧墖瑙f瀽鍐呭鍥炲啓鍒皃ic琛�
+            picMapper.update(new LambdaUpdateWrapper<KnowledgeAttachPic>()
+                .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<KnowledgeAttachPic>()
+                      .ne(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_30)
+                      .eq(KnowledgeAttachPic::getAid, picItem.getAid())
+              );
+              if (nonStatus30Count == 0) {
+                // 鎵ц琛ㄦ洿鏂版搷浣�
+                attachMapper.update(new LambdaUpdateWrapper<KnowledgeAttach>()
+                    .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<KnowledgeAttachPic>()
+          .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<KnowledgeAttachPic>()
+          .set(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_10)
+          .eq(KnowledgeAttachPic::getPicAnysStatus, DealStatus.STATUS_40));
+    }
+  }
+
+
+  /**
+   * 浠庢枃浠跺悕涓彁鍙杙age鍚庨潰鐨勪袱涓暟瀛�
+   *
+   * @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();
+
+      // 瑙f瀽JSON瀛楃涓�
+      JsonNode rootNode = objectMapper.readTree(jsonString);
+
+      // 鑾峰彇choices鏁扮粍鐨勭涓�涓厓绱�
+      JsonNode choicesNode = rootNode.get("choices");
+      if (choicesNode != null && choicesNode.isArray() && choicesNode.size() > 0) {
+        // 鑾峰彇绗竴涓猚hoice
+        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 "瑙f瀽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 d94d320..fb237b2 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,43 +1,62 @@
 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.*;
+
 
 /**
  * 鐭ヨ瘑搴揝ervice涓氬姟灞傚鐞�
@@ -49,216 +68,330 @@
 @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<KnowledgeInfoVo> queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) {
+    LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo);
+    Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+    return TableDataInfo.build(result);
+  }
+
+  /**
+   * 鏌ヨ鐭ヨ瘑搴撳垪琛�
+   */
+  @Override
+  public List<KnowledgeInfoVo> queryList(KnowledgeInfoBo bo) {
+    LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo);
+    return baseMapper.selectVoList(lqw);
+  }
+
+  private LambdaQueryWrapper<KnowledgeInfo> buildQueryWrapper(KnowledgeInfoBo bo) {
+    Map<String, Object> params = bo.getParams();
+    LambdaQueryWrapper<KnowledgeInfo> 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<Long> 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<String, Object> map = new HashMap<>();
+    map.put("kid", id);
+    List<KnowledgeInfoVo> knowledgeInfoList = baseMapper.selectVoByMap(map);
+    check(knowledgeInfoList);
+    // 鍒犻櫎鍚戦噺搴撲俊鎭�
+    knowledgeInfoList.forEach(knowledgeInfoVo -> {
+      vectorStoreService.removeByKid(String.valueOf(knowledgeInfoVo.getId()),
+          knowledgeInfoVo.getVectorModelName());
+    });
+    // 鍒犻櫎闄勪欢鍜岀煡璇嗙墖娈�
+    fragmentMapper.deleteByMap(map);
+    List<KnowledgeAttachVo> knowledgeAttachVos = attachMapper.selectVoByMap(map);
+    if (ObjectUtil.isNotEmpty(knowledgeAttachVos)) {
+      Collection<Long> ossIds = knowledgeAttachVos.stream()
+          .map(KnowledgeAttachVo::getOssId)
+          .collect(Collectors.toList());
+      //鍒犻櫎oss
+      ossService.deleteWithValidByIds(ossIds, false);
+
+      //鍒犻櫎鍥剧墖oss
+      List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
+          new LambdaQueryWrapper<KnowledgeAttachPic>()
+              .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<Long> tossIds = knowledgeAttachPics.stream()
+            .map(KnowledgeAttachPic::getOssId)
+            .collect(Collectors.toList());
+        ossService.deleteWithValidByIds(tossIds, false);
+        List<Long> 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<KnowledgeInfoVo> queryPageList(KnowledgeInfoBo bo, PageQuery pageQuery) {
-        LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo);
-        Page<KnowledgeInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
-        return TableDataInfo.build(result);
-    }
-
-    /**
-     * 鏌ヨ鐭ヨ瘑搴撳垪琛�
-     */
-    @Override
-    public List<KnowledgeInfoVo> queryList(KnowledgeInfoBo bo) {
-        LambdaQueryWrapper<KnowledgeInfo> lqw = buildQueryWrapper(bo);
-        return baseMapper.selectVoList(lqw);
-    }
-
-    private LambdaQueryWrapper<KnowledgeInfo> buildQueryWrapper(KnowledgeInfoBo bo) {
-        Map<String, Object> params = bo.getParams();
-        LambdaQueryWrapper<KnowledgeInfo> 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<String> 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<String> fids = new ArrayList<>();
+    try {
+      content = resourceLoader.getContent(file.getInputStream());
+      chunkList = resourceLoader.getChunkList(content, kid);
+      List<KnowledgeFragment> 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鏂囦欢 鎵嶉渶瑕佹媶瑙e浘鐗囧拰鍒嗘瀽鍥剧墖鍐呭
+      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<KnowledgeInfoVo> knowledgeInfoList) {
+    LoginUser loginUser = LoginHelper.getLoginUser();
+    for (KnowledgeInfoVo knowledgeInfoVo : knowledgeInfoList) {
+      if (!knowledgeInfoVo.getUid().equals(loginUser.getUserId())) {
+        throw new SecurityException("鏉冮檺涓嶈冻");
+      }
     }
+  }
 
-    /**
-     * 鎵归噺鍒犻櫎鐭ヨ瘑搴�
-     */
-    @Override
-    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
-        if(isValid){
-            //TODO 鍋氫竴浜涗笟鍔′笂鐨勬牎楠�,鍒ゆ柇鏄惁闇�瑕佹牎楠�
-        }
-        return baseMapper.deleteBatchIds(ids) > 0;
+
+  /**
+   * 绗竴姝� 瀹氭椂 鎷嗚ВPDF鏂囦欢涓殑鍥剧墖
+   */
+  @Scheduled(fixedDelay = 15000) // 姣�3绉掓墽琛屼竴娆�
+  public void dealKnowledgeAttachPic() throws Exception {
+    //澶勭悊 鎷嗚ВPDF鏂囦欢涓殑鍥剧墖鐨勮褰�
+    List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
+        new LambdaQueryWrapper<KnowledgeAttach>()
+            .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);
-        }
+
+  /**
+   * 绗簩姝� 瀹氭椂 瑙f瀽鍥剧墖鍐呭
+   */
+  @Scheduled(fixedDelay = 15000)
+  public void dealKnowledgeAttachPicAnys() throws Exception {
+    //鑾峰彇鏈鐞嗙殑鍥剧墖璁板綍
+    List<KnowledgeAttachPic> knowledgeAttachPics = picMapper.selectList(
+        new LambdaQueryWrapper<KnowledgeAttachPic>()
+            .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<String,Object> map = new HashMap<>();
-        map.put("kid",id);
-        List<KnowledgeInfoVo> 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);
+
+  /**
+   * 绗笁姝� 瀹氭椂 澶勭悊 闄勪欢涓婁紶鍚庝笂浼犲悜閲忔暟鎹簱
+   */
+  @Scheduled(fixedDelay = 30000) // 姣�3绉掓墽琛屼竴娆�
+  public void dealKnowledgeAttachVector() throws Exception {
+    //澶勭悊 闇�瑕佷笂浼犲悜閲忔暟鎹簱鐨勮褰�
+    List<KnowledgeAttach> knowledgeAttaches = attachMapper.selectList(
+        new LambdaQueryWrapper<KnowledgeAttach>()
+            .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);
+      }
     }
-
-    @Override
-    public void upload(KnowledgeInfoUploadBo bo) {
-        storeContent(bo.getFile(), bo.getKid());
-    }
-
-    public void storeContent(MultipartFile file, String kid) {
-        String fileName = file.getOriginalFilename();
-        List<String> 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<String> fids = new ArrayList<>();
-        try {
-            content = resourceLoader.getContent(file.getInputStream());
-            chunkList = resourceLoader.getChunkList(content, kid);
-            List<KnowledgeFragment> 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.<KnowledgeInfo>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);
-    }
+  }
 
 
-    /**
-     * 妫�鏌ョ敤鎴锋槸鍚︽湁鍒犻櫎鐭ヨ瘑搴撴潈闄�
-     *
-     * @param knowledgeInfoList 鐭ヨ瘑搴撳垪琛�
-     */
-    public void check(List<KnowledgeInfoVo> 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 2cb86be..e1b81b2 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`;

--
Gitblit v1.9.3