From 32da85daabbb0e574f12bc7f1e0af6ff5b0cee6a Mon Sep 17 00:00:00 2001
From: zhouweiyi
Date: 星期二, 13 五月 2025 10:55:39 +0800
Subject: [PATCH] 提取PDF中的图片并调用大模型,识别图片内容并返回

---
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java                          |   95 ++++++++++
 ruoyi-admin/src/main/resources/application-prod.yml                                                        |    5 
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java |  144 ++++++++++++++++
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java          |   41 ++++
 ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/PdfFileContentResult.java             |   30 +++
 ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java        |  219 +++++++++++++----------
 ruoyi-admin/src/main/resources/application-dev.yml                                                         |    5 
 7 files changed, 441 insertions(+), 98 deletions(-)

diff --git a/ruoyi-admin/src/main/resources/application-dev.yml b/ruoyi-admin/src/main/resources/application-dev.yml
index 2a4613f..37c944f 100644
--- a/ruoyi-admin/src/main/resources/application-dev.yml
+++ b/ruoyi-admin/src/main/resources/application-dev.yml
@@ -94,3 +94,8 @@
   # 鑵捐涓撶敤
   sdkAppId:
 
+pdf:
+  extract:
+    service:
+      url: http://localhost:8080
+
diff --git a/ruoyi-admin/src/main/resources/application-prod.yml b/ruoyi-admin/src/main/resources/application-prod.yml
index cd52c0f..f157186 100644
--- a/ruoyi-admin/src/main/resources/application-prod.yml
+++ b/ruoyi-admin/src/main/resources/application-prod.yml
@@ -172,3 +172,8 @@
   signName: 娴嬭瘯
   # 鑵捐涓撶敤
   sdkAppId:
+
+pdf:
+  extract:
+    service:
+      url: http://localhost:8080
\ No newline at end of file
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/PdfFileContentResult.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/PdfFileContentResult.java
new file mode 100644
index 0000000..d6594e4
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/domain/PdfFileContentResult.java
@@ -0,0 +1,30 @@
+package org.ruoyi.domain;
+
+/**
+ * 鏂囦欢鍐呭缁撴灉灏佽绫�
+ */
+public class PdfFileContentResult {
+    private String filename;
+    private String content;
+
+    public PdfFileContentResult(String filename, String content) {
+        this.filename = filename;
+        this.content = content;
+    }
+
+    public String getFilename() {
+        return filename;
+    }
+
+    public void setFilename(String filename) {
+        this.filename = filename;
+    }
+
+    public String getContent() {
+        return content;
+    }
+
+    public void setContent(String content) {
+        this.content = content;
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java
new file mode 100644
index 0000000..759b439
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java
@@ -0,0 +1,41 @@
+package org.ruoyi.service;
+
+import java.io.IOException;
+import java.util.List;
+import org.ruoyi.domain.PdfFileContentResult;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * PDF鍥剧墖鎻愬彇鏈嶅姟鎺ュ彛
+ */
+public interface PdfImageExtractService {
+
+  /**
+   * 浠嶱DF鏂囦欢涓彁鍙栧浘鐗�
+   *
+   * @param pdfFile PDF鏂囦欢
+   * @param imageFormat 杈撳嚭鍥剧墖鏍煎紡 (png, jpeg, gif)
+   * @param allowDuplicates 鏄惁鍏佽閲嶅鍥剧墖
+   * @return 鍖呭惈鎻愬彇鍥剧墖鐨刏IP鏂囦欢鐨勫瓧鑺傛暟缁�
+   * @throws IOException 濡傛灉鏂囦欢澶勭悊杩囩▼涓彂鐢熼敊璇�
+   */
+  byte[] extractImages(MultipartFile pdfFile, String imageFormat, boolean allowDuplicates)
+      throws IOException;
+
+  /**
+   * 澶勭悊鏂囦欢鍐呭
+   *
+   * @param unzip Base64缂栫爜鐨勫浘鐗囨暟缁�
+   * @return 鏂囦欢鍐呭缁撴灉鍒楄〃
+   * @throws IOException 濡傛灉API璋冪敤杩囩▼涓彂鐢熼敊璇�
+   */
+  List<PdfFileContentResult> dealFileContent(String[] unzip) throws IOException;
+
+  /**
+   * 鎻愬彇PDF涓殑鍥剧墖骞惰皟鐢╣pt-4o-mini,璇嗗埆鍥剧墖鍐呭骞惰繑鍥�
+   * @param file
+   * @return
+   * @throws IOException
+   */
+  List<PdfFileContentResult> extractImages(MultipartFile file) throws IOException;
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..55ce87e
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java
@@ -0,0 +1,144 @@
+package org.ruoyi.service.impl;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.MediaType;
+import okhttp3.MultipartBody;
+import okhttp3.OkHttpClient;
+import okhttp3.OkHttpClient.Builder;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import org.ruoyi.common.core.domain.R;
+import org.ruoyi.domain.PdfFileContentResult;
+import org.ruoyi.service.PdfImageExtractService;
+import org.ruoyi.utils.ZipUtils;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * PDF鍥剧墖鎻愬彇鏈嶅姟瀹炵幇绫�
+ */
+@Service
+@Slf4j
+public class PdfImageExtractServiceImpl implements PdfImageExtractService {
+
+  @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 ;
+
+  private final OkHttpClient client = new Builder()
+      .connectTimeout(100, TimeUnit.SECONDS)
+      .readTimeout(150, TimeUnit.SECONDS)
+      .writeTimeout(150, TimeUnit.SECONDS)
+      .callTimeout(300, TimeUnit.SECONDS)
+      .build();
+
+  private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
+
+  @Override
+  public byte[] extractImages(MultipartFile pdfFile, String imageFormat, boolean allowDuplicates)
+      throws IOException {
+    // 鏋勫缓multipart璇锋眰
+    RequestBody requestBody = new MultipartBody.Builder()
+        .setType(MultipartBody.FORM)
+        .addFormDataPart("fileInput", pdfFile.getOriginalFilename(),
+            RequestBody.create(MediaType.parse("application/pdf"), pdfFile.getBytes()))
+        .addFormDataPart("format", imageFormat)
+        .addFormDataPart("allowDuplicates", String.valueOf(allowDuplicates))
+        .build();
+
+    // 鍒涘缓璇锋眰
+    Request request = new Request.Builder()
+        .url(serviceUrl + "/api/v1/misc/extract-images")
+        .post(requestBody)
+        .build();
+
+    // 鎵ц璇锋眰
+    try (Response response = client.newCall(request).execute()) {
+      if (!response.isSuccessful()) {
+        throw new IOException("璇锋眰澶辫触: " + response.code());
+      }
+      return response.body().bytes();
+    }
+  }
+
+  /**
+   * 澶勭悊鏂囦欢鍐呭
+   *
+   * @param unzip Base64缂栫爜鐨勫浘鐗囨暟缁�
+   * @return 鏂囦欢鍐呭缁撴灉鍒楄〃
+   * @throws IOException 濡傛灉API璋冪敤杩囩▼涓彂鐢熼敊璇�
+   */
+  @Override
+  public List<PdfFileContentResult> dealFileContent(String[] unzip) throws IOException {
+    List<PdfFileContentResult> results = new ArrayList<>();
+    int i = 0;
+    for (String base64Image : unzip) {
+      // 鏋勫缓璇锋眰JSON
+      String requestJson = String.format("{"
+          + "\"model\": \"gpt-4o\","
+          + "\"stream\": false,"
+          + "\"messages\": [{"
+          + "\"role\": \"user\","
+          + "\"content\": [{"
+          + "\"type\": \"text\","
+          + "\"text\": \"杩欏紶鍥剧墖鏈変粈涔圽""
+          + "}, {"
+          + "\"type\": \"image_url\","
+          + "\"image_url\": {"
+          + "\"url\": \"%s\""
+          + "}}"
+          + "]}],"
+          + "\"max_tokens\": 400"
+          + "}", base64Image);
+
+      // 鍒涘缓璇锋眰
+      Request request = new Request.Builder()
+          .url(aiApiUrl)
+          .addHeader("Authorization", "Bearer " + aiApiKey)
+          .post(RequestBody.create(JSON, requestJson))
+          .build();
+
+      // 鎵ц璇锋眰
+      try {
+        log.info("=============call=" + ++i);
+        Response response = client.newCall(request).execute();
+        log.info("=============response=" + response);
+        if (!response.isSuccessful()) {
+          throw new IOException("API璇锋眰澶辫触: " + response.code() + response.toString());
+        }
+
+        String responseBody = response.body().string();
+        log.info("=============responseBody=" + responseBody);
+        // 浣跨敤鏂囦欢鍚嶏紙杩欓噷浣跨敤base64鐨勫墠10涓瓧绗︿綔涓烘爣璇嗭級鍜孉PI杩斿洖鍐呭鍒涘缓缁撴灉瀵硅薄
+        String filename = base64Image.substring(0, Math.min(base64Image.length(), 10));
+        results.add(new PdfFileContentResult(filename, responseBody));
+      } catch (Exception e) {
+        log.error(e.getMessage());
+        throw new RuntimeException(e);
+      }
+    }
+
+    return results;
+  }
+
+  @Override
+  public List<PdfFileContentResult> extractImages(MultipartFile file) throws IOException {
+    String format = "png";
+    boolean allowDuplicates = true;
+    // 鑾峰彇ZIP鏁版嵁
+    byte[] zipData = this.extractImages(file, format, allowDuplicates);
+    // 瑙e帇鏂囦欢骞惰瘑鍒浘鐗囧唴瀹瑰苟杩斿洖
+    String[] unzip = ZipUtils.unzipForBase64(zipData);
+    //瑙f瀽鍥剧墖鍐呭
+    return this.dealFileContent(unzip);
+  }
+}
\ No newline at end of file
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
new file mode 100644
index 0000000..7ae1b2b
--- /dev/null
+++ b/ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/utils/ZipUtils.java
@@ -0,0 +1,95 @@
+package org.ruoyi.utils;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * ZIP鏂囦欢澶勭悊宸ュ叿绫�
+ */
+public class ZipUtils {
+
+    /**
+     * 瑙e帇ZIP鏂囦欢鍒版寚瀹氱洰褰�
+     *
+     * @param zipData ZIP鏂囦欢鐨勫瓧鑺傛暟缁�
+     * @param destDir 鐩爣鐩綍
+     * @return 瑙e帇鍚庣殑鏂囦欢璺緞鍒楄〃
+     * @throws IOException 濡傛灉瑙e帇杩囩▼涓彂鐢熼敊璇�
+     */
+    public static String[] unzip(byte[] zipData, String destDir) throws IOException {
+        File destDirFile = new File(destDir);
+        if (!destDirFile.exists()) {
+            destDirFile.mkdirs();
+        }
+
+        List<String> extractedPaths = new ArrayList<>();
+        try (ByteArrayInputStream bis = new ByteArrayInputStream(zipData);
+             ZipInputStream zis = new ZipInputStream(bis)) {
+
+            ZipEntry zipEntry;
+            while ((zipEntry = zis.getNextEntry()) != null) {
+                String filePath = destDir + File.separator + zipEntry.getName();
+                if (!zipEntry.isDirectory()) {
+                    extractFile(zis, filePath);
+                    extractedPaths.add(filePath);
+                } else {
+                    new File(filePath).mkdirs();
+                }
+                zis.closeEntry();
+            }
+        }
+        return extractedPaths.toArray(new String[0]);
+    }
+
+    private static void extractFile(ZipInputStream zis, String filePath) throws IOException {
+        try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath))) {
+            byte[] buffer = new byte[4096];
+            int read;
+            while ((read = zis.read(buffer)) != -1) {
+                bos.write(buffer, 0, read);
+            }
+        }
+    }
+
+    /**
+     * 瑙e帇ZIP鏂囦欢骞惰繑鍥炴枃浠跺唴瀹圭殑Base64缂栫爜瀛楃涓叉暟缁�
+     *
+     * @param zipData ZIP鏂囦欢鐨勫瓧鑺傛暟缁�
+     * @return Base64缂栫爜鐨勬枃浠跺唴瀹规暟缁�
+     * @throws IOException 濡傛灉瑙e帇杩囩▼涓彂鐢熼敊璇�
+     */
+    public static String[] unzipForBase64(byte[] zipData) throws IOException {
+        List<String> base64Contents = 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);
+                    }
+                    
+                    // 灏嗘枃浠跺唴瀹硅浆鎹负Base64瀛楃涓�
+                    String base64Content = Base64.getEncoder().encodeToString(baos.toByteArray());
+                    base64Contents.add(base64Content);
+                }
+                zis.closeEntry();
+            }
+        }
+        return base64Contents.toArray(new String[0]);
+    }
+}
\ No newline at end of file
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 e573c82..d8066e7 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
@@ -1,9 +1,12 @@
 package org.ruoyi.chat.controller.knowledge;
 
 import cn.dev33.satoken.stp.StpUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
 import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
+import java.io.IOException;
 import lombok.RequiredArgsConstructor;
 import org.ruoyi.common.core.domain.R;
 import org.ruoyi.common.core.validate.AddGroup;
@@ -14,6 +17,7 @@
 import org.ruoyi.common.web.core.BaseController;
 import org.ruoyi.core.page.PageQuery;
 import org.ruoyi.core.page.TableDataInfo;
+import org.ruoyi.domain.PdfFileContentResult;
 import org.ruoyi.domain.bo.KnowledgeAttachBo;
 import org.ruoyi.domain.bo.KnowledgeFragmentBo;
 import org.ruoyi.domain.bo.KnowledgeInfoBo;
@@ -24,6 +28,7 @@
 import org.ruoyi.service.IKnowledgeAttachService;
 import org.ruoyi.service.IKnowledgeFragmentService;
 import org.ruoyi.service.IKnowledgeInfoService;
+import org.ruoyi.service.PdfImageExtractService;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
@@ -41,117 +46,135 @@
 @RequestMapping("/knowledge")
 public class KnowledgeController extends BaseController {
 
-    private final IKnowledgeInfoService knowledgeInfoService;
+  private final IKnowledgeInfoService knowledgeInfoService;
 
-    private final IKnowledgeAttachService attachService;
+  private final IKnowledgeAttachService attachService;
 
-    private final IKnowledgeFragmentService fragmentService;
+  private final IKnowledgeFragmentService fragmentService;
 
-    /**
-     * 鏍规嵁鐢ㄦ埛淇℃伅鏌ヨ鏈湴鐭ヨ瘑搴�
-     */
-    @GetMapping("/list")
-    public TableDataInfo<KnowledgeInfoVo> list(KnowledgeInfoBo bo, PageQuery pageQuery) {
-        if (!StpUtil.isLogin()) {
-            throw new SecurityException("璇峰厛鍘荤櫥褰�!");
-        }
-        bo.setUid(LoginHelper.getUserId());
-        return knowledgeInfoService.queryPageList(bo, pageQuery);
+  private final PdfImageExtractService pdfImageExtractService;
+
+  /**
+   * 鏍规嵁鐢ㄦ埛淇℃伅鏌ヨ鏈湴鐭ヨ瘑搴�
+   */
+  @GetMapping("/list")
+  public TableDataInfo<KnowledgeInfoVo> list(KnowledgeInfoBo bo, PageQuery pageQuery) {
+    if (!StpUtil.isLogin()) {
+      throw new SecurityException("璇峰厛鍘荤櫥褰�!");
     }
+    bo.setUid(LoginHelper.getUserId());
+    return knowledgeInfoService.queryPageList(bo, pageQuery);
+  }
 
-    /**
-     * 鏂板鐭ヨ瘑搴�
-     */
-    @Log(title = "鐭ヨ瘑搴�", businessType = BusinessType.INSERT)
-    @PostMapping("/save")
-    public R<Void> save(@Validated(AddGroup.class) @RequestBody KnowledgeInfoBo bo) {
-        knowledgeInfoService.saveOne(bo);
-        return R.ok();
-    }
+  /**
+   * 鏂板鐭ヨ瘑搴�
+   */
+  @Log(title = "鐭ヨ瘑搴�", businessType = BusinessType.INSERT)
+  @PostMapping("/save")
+  public R<Void> save(@Validated(AddGroup.class) @RequestBody KnowledgeInfoBo bo) {
+    knowledgeInfoService.saveOne(bo);
+    return R.ok();
+  }
 
-    /**
-     * 鍒犻櫎鐭ヨ瘑搴�
-     */
-    @PostMapping("/remove/{id}")
-    public R<String> remove(@PathVariable String id) {
-        knowledgeInfoService.removeKnowledge(id);
-        return R.ok("鍒犻櫎鐭ヨ瘑搴撴垚鍔�!");
-    }
+  /**
+   * 鍒犻櫎鐭ヨ瘑搴�
+   */
+  @PostMapping("/remove/{id}")
+  public R<String> remove(@PathVariable String id) {
+    knowledgeInfoService.removeKnowledge(id);
+    return R.ok("鍒犻櫎鐭ヨ瘑搴撴垚鍔�!");
+  }
 
-    /**
-     * 淇敼鐭ヨ瘑搴�
-     */
-    @Log(title = "鐭ヨ瘑搴�", businessType = BusinessType.UPDATE)
-    @PostMapping("/edit")
-    public R<Void> edit(@RequestBody KnowledgeInfoBo bo) {
-        return toAjax(knowledgeInfoService.updateByBo(bo));
-    }
+  /**
+   * 淇敼鐭ヨ瘑搴�
+   */
+  @Log(title = "鐭ヨ瘑搴�", businessType = BusinessType.UPDATE)
+  @PostMapping("/edit")
+  public R<Void> edit(@RequestBody KnowledgeInfoBo bo) {
+    return toAjax(knowledgeInfoService.updateByBo(bo));
+  }
 
-    /**
-     * 瀵煎嚭鐭ヨ瘑搴撳垪琛�
-     */
-    @Log(title = "鐭ヨ瘑搴�", businessType = BusinessType.EXPORT)
-    @PostMapping("/export")
-    public void export(KnowledgeInfoBo bo, HttpServletResponse response) {
-        List<KnowledgeInfoVo> list = knowledgeInfoService.queryList(bo);
-        ExcelUtil.exportExcel(list, "鐭ヨ瘑搴�", KnowledgeInfoVo.class, response);
-    }
+  /**
+   * 瀵煎嚭鐭ヨ瘑搴撳垪琛�
+   */
+  @Log(title = "鐭ヨ瘑搴�", businessType = BusinessType.EXPORT)
+  @PostMapping("/export")
+  public void export(KnowledgeInfoBo bo, HttpServletResponse response) {
+    List<KnowledgeInfoVo> list = knowledgeInfoService.queryList(bo);
+    ExcelUtil.exportExcel(list, "鐭ヨ瘑搴�", KnowledgeInfoVo.class, response);
+  }
 
-    /**
-     * 鏌ヨ鐭ヨ瘑闄勪欢淇℃伅
-     */
-    @GetMapping("/detail/{kid}")
-    public TableDataInfo<KnowledgeAttachVo> attach(KnowledgeAttachBo bo, PageQuery pageQuery, @PathVariable String kid) {
-        bo.setKid(kid);
-        return attachService.queryPageList(bo, pageQuery);
-    }
+  /**
+   * 鏌ヨ鐭ヨ瘑闄勪欢淇℃伅
+   */
+  @GetMapping("/detail/{kid}")
+  public TableDataInfo<KnowledgeAttachVo> attach(KnowledgeAttachBo bo, PageQuery pageQuery,
+      @PathVariable String kid) {
+    bo.setKid(kid);
+    return attachService.queryPageList(bo, pageQuery);
+  }
 
-    /**
-     * 涓婁紶鐭ヨ瘑搴撻檮浠�
-     */
-    @PostMapping(value = "/attach/upload")
-    public R<String> upload(KnowledgeInfoUploadBo bo) {
-        knowledgeInfoService.upload(bo);
-        return R.ok("涓婁紶鐭ヨ瘑搴撻檮浠舵垚鍔�!");
-    }
+  /**
+   * 涓婁紶鐭ヨ瘑搴撻檮浠�
+   */
+  @PostMapping(value = "/attach/upload")
+  public R<String> upload(KnowledgeInfoUploadBo bo) {
+    knowledgeInfoService.upload(bo);
+    return R.ok("涓婁紶鐭ヨ瘑搴撻檮浠舵垚鍔�!");
+  }
 
-    /**
-     * 鑾峰彇鐭ヨ瘑搴撻檮浠惰缁嗕俊鎭�
-     *
-     * @param id 涓婚敭
-     */
-    @GetMapping("attach/info/{id}")
-    public R<KnowledgeAttachVo> getAttachInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
-                                              @PathVariable Long id) {
-        return R.ok(attachService.queryById(id));
-    }
+  /**
+   * 鑾峰彇鐭ヨ瘑搴撻檮浠惰缁嗕俊鎭�
+   *
+   * @param id 涓婚敭
+   */
+  @GetMapping("attach/info/{id}")
+  public R<KnowledgeAttachVo> getAttachInfo(@NotNull(message = "涓婚敭涓嶈兘涓虹┖")
+  @PathVariable Long id) {
+    return R.ok(attachService.queryById(id));
+  }
 
-    /**
-     * 鍒犻櫎鐭ヨ瘑搴撻檮浠�
-     */
-    @PostMapping("attach/remove/{kid}")
-    public R<Void> removeAttach(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
-                                @PathVariable String kid) {
-        attachService.removeKnowledgeAttach(kid);
-        return R.ok();
-    }
+  /**
+   * 鍒犻櫎鐭ヨ瘑搴撻檮浠�
+   */
+  @PostMapping("attach/remove/{kid}")
+  public R<Void> removeAttach(@NotEmpty(message = "涓婚敭涓嶈兘涓虹┖")
+  @PathVariable String kid) {
+    attachService.removeKnowledgeAttach(kid);
+    return R.ok();
+  }
 
 
-    /**
-     * 鏌ヨ鐭ヨ瘑鐗囨
-     */
-    @GetMapping("/fragment/list/{docId}")
-    public TableDataInfo<KnowledgeFragmentVo> fragmentList(KnowledgeFragmentBo bo, PageQuery pageQuery, @PathVariable String docId) {
-        bo.setDocId(docId);
-        return fragmentService.queryPageList(bo, pageQuery);
-    }
+  /**
+   * 鏌ヨ鐭ヨ瘑鐗囨
+   */
+  @GetMapping("/fragment/list/{docId}")
+  public TableDataInfo<KnowledgeFragmentVo> fragmentList(KnowledgeFragmentBo bo,
+      PageQuery pageQuery, @PathVariable String docId) {
+    bo.setDocId(docId);
+    return fragmentService.queryPageList(bo, pageQuery);
+  }
 
-    /**
-     * 涓婁紶鏂囦欢缈昏瘧
-     */
-    @PostMapping("/translationByFile")
-    @ResponseBody
-    public String translationByFile(@RequestParam("file") MultipartFile file, String targetLanguage) {
-        return attachService.translationByFile(file, targetLanguage);
-    }
+  /**
+   * 涓婁紶鏂囦欢缈昏瘧
+   */
+  @PostMapping("/translationByFile")
+  @ResponseBody
+  public String translationByFile(@RequestParam("file") MultipartFile file, String targetLanguage) {
+    return attachService.translationByFile(file, targetLanguage);
+  }
+
+  /**
+   * 鎻愬彇PDF涓殑鍥剧墖骞惰皟鐢╣pt-4o-mini,璇嗗埆鍥剧墖鍐呭骞惰繑鍥�
+   *
+   * @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));
+  }
 }

--
Gitblit v1.9.3