diff --git a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java
index ed55f5f39..6db7c1945 100644
--- a/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java
+++ b/jero-boot/jero-boot-base/jero-boot-base-core/src/main/java/com/jero/config/shiro/ShiroConfig.java
@@ -72,6 +72,8 @@ public class ShiroConfig {
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/sys/cas/client/validateLogin", "anon"); //cas验证登录
filterChainDefinitionMap.put("/ocr/OcrRestful/ocrHandleResult", "anon"); //ocr回调接口
+ filterChainDefinitionMap.put("/ocr/ocrCheck/downFile", "anon"); //ocr校核下载文件
+ filterChainDefinitionMap.put("/ocr/ocrCheck/saveFile", "anon"); //ocr校核回调
filterChainDefinitionMap.put("/sys/randomImage/**", "anon"); //登录验证码接口排除
filterChainDefinitionMap.put("/sys/checkCaptcha", "anon"); //登录验证码接口排除
filterChainDefinitionMap.put("/sys/getRSAPublicKey", "anon"); //获取RSA公钥接口排除
diff --git a/jero-boot/jero-boot-modules/pom.xml b/jero-boot/jero-boot-modules/pom.xml
index 82f9ecf17..d1605c902 100644
--- a/jero-boot/jero-boot-modules/pom.xml
+++ b/jero-boot/jero-boot-modules/pom.xml
@@ -150,6 +150,17 @@
org.springframework.boot
spring-boot-starter-websocket
+
+
+ com.googlecode.json-simple
+ json-simple
+ 1.1
+
+
+ com.inversoft
+ prime-jwt
+ 1.3.1
+
diff --git a/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/controller/OcrCheckController.java b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/controller/OcrCheckController.java
new file mode 100644
index 000000000..e1f35b555
--- /dev/null
+++ b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/controller/OcrCheckController.java
@@ -0,0 +1,229 @@
+package com.jero.modules.ocr.controller;
+
+import com.jero.common.exception.JeroBootException;
+import com.jero.common.system.vo.LoginUser;
+import com.jero.modules.ocr.helpers.ConfigManager;
+import com.jero.modules.ocr.helpers.DocumentManager;
+import com.jero.modules.ocr.helpers.FileUtility;
+import com.jero.modules.ocr.helpers.ServiceConverter;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.IOUtils;
+import org.apache.shiro.SecurityUtils;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Scanner;
+
+/**
+ * @Author: liyawei
+ * @Description:
+ * @Date: Created in 10:51 2022/4/11
+ */
+@Api(tags="OCR识别在线校核")
+@RestController
+@RequestMapping("/ocr/ocrCheck")
+@Slf4j
+public class OcrCheckController {
+
+ @Value("${OCR.ocrDownPath}")
+ private String ocrDownPath;
+
+ @Value("${OCR.ocrSavePath}")
+ private String ocrSavePath;
+
+ @Value("${OCR.serverUrl}")
+ private String serverUrl;
+
+ @Value("${OCR.ocrPath}")
+ private String ocrPath;
+
+ @ApiOperation(value = "下载word文件")
+ @GetMapping("/downFile")
+ public void downFile(String fileName, HttpServletResponse response, HttpServletRequest request) throws Exception {
+ InputStream is = null;
+ OutputStream os = null;
+ response.reset();
+ try {
+ String downFileName = fileName.substring(fileName.indexOf("_") + 1);
+ response.setHeader("Content-Disposition", "attachment; filename=" + downFileName);
+ response.setContentType("application/octet-stream");
+
+ String fullPath = ocrPath + fileName;
+ is = new FileInputStream(fullPath);
+ os = response.getOutputStream();
+ IOUtils.copy(is, os);
+ os.flush();
+ } catch (FileNotFoundException var4) {
+ throw new JeroBootException("文件[" + ocrPath + fileName + "]不存在");
+ } catch (IOException e) {
+ log.error(e.getMessage(), e);
+ } finally {
+ is.close();
+ os.close();
+ }
+ }
+
+ @ApiOperation(value = "编辑文件")
+ @RequestMapping("/editorFile")
+ public ModelAndView index(HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
+
+ String fileName = "";
+ if (request.getParameterMap().containsKey("fileName")) {
+ fileName = request.getParameter("fileName");
+ }
+// String fileExt = null;
+// if (request.getParameterMap().containsKey("fileExt")) {
+// fileExt = request.getParameter("fileExt");
+// }
+
+// if (fileExt != null) {
+// try {
+// DocumentManager.Init(request, response);
+// fileName = DocumentManager.CreateDemo(fileExt);
+// } catch (Exception ex) {
+// return new ModelAndView(new FastJsonJsonView(),"Error: " + ex.getMessage(), ex) ;
+// }
+// }
+
+// String mode = "";
+// if (request.getParameterMap().containsKey("mode"))
+// {
+// mode = request.getParameter("mode");
+// }
+// Boolean desktopMode = !"embedded".equals(mode);
+
+// FileModel file = new FileModel();
+// file.SetTypeDesktop(desktopMode);
+// file.SetFileName(fileName);
+
+ log.info("==========EditorFile==========");
+ DocumentManager.Init(request, response);
+ //要编辑的文件名
+ model.addAttribute("fileName", fileName) ;
+ //要编辑的文件类型
+ model.addAttribute("fileType", FileUtility.GetFileExtension(fileName).replace(".", "")) ;
+ //要编辑的文档类型
+ model.addAttribute("documentType",FileUtility.GetFileType(fileName).toString().toLowerCase()) ;
+ //要编辑的文档访问url
+// model.addAttribute("fileUri",DocumentManager.GetFileUri(fileName, true)) ;
+// model.addAttribute("callbackUrl", DocumentManager.GetCallback(fileName)) ;
+// model.addAttribute("serverUrl", DocumentManager.GetServerUrl(true)) ;
+ model.addAttribute("fileUri", ocrDownPath + "?fileName=" + fileName) ;
+ model.addAttribute("callbackUrl", ocrSavePath + "?fileName=" + fileName) ;
+ model.addAttribute("serverUrl", serverUrl) ;
+ model.addAttribute("fileKey", ServiceConverter.GenerateRevisionId(DocumentManager.CurUserHostAddress(null) + ocrDownPath + "?fileName=" + fileName)) ;
+ model.addAttribute("editorMode", DocumentManager.GetEditedExts().contains(FileUtility.GetFileExtension(fileName)) && !"view".equals(request.getAttribute("mode")) ? "edit" : "view") ;
+ model.addAttribute("editorUserId",DocumentManager.CurUserHostAddress(null)) ;
+ LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
+ model.addAttribute("editorUserName", loginUser.getUsername()) ;
+
+// model.addAttribute("type", desktopMode ? "desktop" : "embedded");
+ model.addAttribute("type", true ? "desktop" : "embedded");
+ model.addAttribute("docserviceApiUrl", ConfigManager.GetProperty("files.docservice.url.api"));
+ model.addAttribute("docServiceUrlPreloader", ConfigManager.GetProperty("files.docservice.url.preloader")) ;
+ model.addAttribute("currentYear", "2022") ;
+ model.addAttribute("convertExts", String.join(",", DocumentManager.GetConvertExts())) ;
+ model.addAttribute("editedExts", String.join(",", DocumentManager.GetEditedExts())) ;
+ model.addAttribute("documentCreated", new SimpleDateFormat("MM/dd/yyyy").format(new Date())) ;
+ model.addAttribute("permissionsEdit", Boolean.toString(DocumentManager.GetEditedExts().contains(FileUtility.GetFileExtension(fileName))).toLowerCase()) ;
+ return new ModelAndView("editor") ;
+ }
+
+ /**
+ * 文档编辑服务使用JavaScript API通知callbackUrl,向文档存储服务通知文档编辑的状态。文档编辑服务使用具有正文中的信息的POST请求。
+ * https://api.onlyoffice.com/editors/callback
+ * 参数示例:
+ {
+ "actions": [{"type": 0, "userid": "78e1e841"}],
+ "changesurl": "https://documentserver/url-to-changes.zip",
+ "history": {
+ "changes": changes,
+ "serverVersion": serverVersion
+ },
+ "key": "Khirz6zTPdfd7",
+ "status": 2,
+ "url": "https://documentserver/url-to-edited-document.docx",
+ "users": ["6d5a81d0"]
+ }
+ */
+ @ApiOperation(value = "保存文件")
+ @RequestMapping("/saveFile")
+ public void saveFile(HttpServletRequest request, HttpServletResponse response) {
+ String fileName = "";
+ if (request.getParameterMap().containsKey("fileName")) {
+ fileName = request.getParameter("fileName");
+ }
+ PrintWriter writer = null;
+ log.info("==========SaveEditedFile==========");
+ try {
+ writer = response.getWriter();
+ Scanner scanner = new Scanner(request.getInputStream()).useDelimiter("\\A");
+ String body = scanner.hasNext() ? scanner.next() : "";
+ JSONObject jsonObj = (JSONObject) new JSONParser().parse(body);
+ log.info("status:" + jsonObj.get("status"));
+ /*
+ 0 - no document with the key identifier could be found,
+ 1 - document is being edited,
+ 2 - document is ready for saving,
+ 3 - document saving error has occurred,
+ 4 - document is closed with no changes,
+ 6 - document is being edited, but the current document state is saved,
+ 7 - error has occurred while force saving the document.
+ * */
+ if ((long) jsonObj.get("status") == 2) {
+ /*
+ * 当我们关闭编辑窗口后,十秒钟左右onlyoffice会将它存储的我们的编辑后的文件,,此时status = 2,通过request发给我们,我们需要做的就是接收到文件然后回写该文件。
+ * */
+
+ /*
+ * 定义要与文档存储服务保存的编辑文档的链接。当状态值仅等于2或3时,存在链路。
+ * */
+ String downloadUri = (String) jsonObj.get("url");
+ log.info("文档编辑完成,现在开始保存编辑后的文档,其下载地址为:" + downloadUri);
+ //解析得出文件名
+// String fileName = downloadUri.substring(downloadUri.lastIndexOf('/') + 1);
+ log.info("下载的文件名:" + fileName);
+
+ URL url = new URL(downloadUri);
+ java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
+ InputStream stream = connection.getInputStream();
+
+ File savedFile = new File(ocrPath + fileName);
+ try (FileOutputStream out = new FileOutputStream(savedFile)) {
+ int read;
+ final byte[] bytes = new byte[1024];
+ while ((read = stream.read(bytes)) != -1) {
+ out.write(bytes, 0, read);
+ }
+ out.flush();
+ }
+ connection.disconnect();
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (ParseException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ /*
+ * status = 1,我们给onlyoffice的服务返回{"error":"0"}的信息,这样onlyoffice会认为回调接口是没问题的,这样就可以在线编辑文档了,否则的话会弹出窗口说明
+ * */
+ writer.write("{\"error\":0}");
+ }
+}
diff --git a/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/entity/CommentGroups.java b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/entity/CommentGroups.java
new file mode 100644
index 000000000..4a6629f7c
--- /dev/null
+++ b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/entity/CommentGroups.java
@@ -0,0 +1,17 @@
+package com.jero.modules.ocr.entity;
+
+import java.util.List;
+
+public class CommentGroups {
+ public List view;
+ public List edit;
+ public List remove;
+ public CommentGroups(){
+
+ }
+ public CommentGroups(List view, List edit, List remove){
+ this.view = view;
+ this.edit = edit;
+ this.remove = remove;
+ }
+}
diff --git a/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/entity/FileModel.java b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/entity/FileModel.java
new file mode 100644
index 000000000..d45af8a55
--- /dev/null
+++ b/jero-boot/jero-boot-modules/src/main/java/com/jero/modules/ocr/entity/FileModel.java
@@ -0,0 +1,370 @@
+/**
+ *
+ * (c) Copyright Ascensio System SIA 2021
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.jero.modules.ocr.entity;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.jero.modules.ocr.helpers.DocumentManager;
+import com.jero.modules.ocr.helpers.FileUtility;
+import com.jero.modules.ocr.helpers.ServiceConverter;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.*;
+
+public class FileModel
+{
+ public String type = "desktop";
+ public String mode = "edit";
+ public String documentType;
+ public Document document;
+ public EditorConfig editorConfig;
+ public String token;
+
+ // create file model
+ public FileModel(String fileName, String lang, String actionData, User user)
+ {
+ if (fileName == null) fileName = "";
+ fileName = fileName.trim(); // remove extra spaces in the file name
+
+ // get file type from the file name (word, cell or slide)
+ documentType = FileUtility.GetFileType(fileName).toString().toLowerCase();
+
+ // set the document parameters
+ document = new Document();
+ document.title = fileName;
+ document.url = DocumentManager.GetDownloadUrl(fileName); // get file url
+ document.urlUser = DocumentManager.GetFileUri(fileName, false);
+ document.fileType = FileUtility.GetFileExtension(fileName).replace(".", ""); // get file extension from the file name
+ // generate document key
+ document.key = ServiceConverter.GenerateRevisionId(DocumentManager.CurUserHostAddress(null) + "/" + fileName + "/" + Long.toString(new File(DocumentManager.StoragePath(fileName, null)).lastModified()));
+ document.info = new Info();
+ document.info.favorite = user.favorite;
+
+ String templatesImageUrl = DocumentManager.GetTemplateImageUrl(FileUtility.GetFileType(fileName));
+ List