Просмотр исходного кода

- 重写了PDF工具
- 部分完善了扫描页面功能

Huanyi 3 месяцев назад
Родитель
Сommit
c73190a05e
21 измененных файлов с 311 добавлено и 181 удалено
  1. 1 1
      ruoyi-admin/pom.xml
  2. 1 1
      ruoyi-common/pom.xml
  3. 1 1
      ruoyi-common/ruoyi-common-bom/pom.xml
  4. 1 1
      ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java
  5. 1 1
      ruoyi-common/yingpaipay-common-document/pom.xml
  6. 0 0
      ruoyi-common/yingpaipay-common-document/src/main/java/com/yingpaipay/common/file/config/TextInConfig.java
  7. 96 0
      ruoyi-common/yingpaipay-common-document/src/main/java/com/yingpaipay/common/file/util/PdfUtils.java
  8. 0 0
      ruoyi-common/yingpaipay-common-document/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
  9. 0 157
      ruoyi-common/yingpaipay-common-file/src/main/java/com/yingpaipay/common/file/util/WatermarkUtils.java
  10. 1 1
      ruoyi-modules/yingpaipay-business/pom.xml
  11. 18 8
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/applet/AppletScanController.java
  12. 10 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/AppletDocumentScanSubmitBo.java
  13. 18 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/AppletUploadOnSubmitBo.java
  14. 32 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/AppletDocumentScanSubmitVo.java
  15. 3 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentAuditLogVo.java
  16. 4 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentService.java
  17. 10 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/CommonFolderService.java
  18. 11 0
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/CommonProjectService.java
  19. 101 8
      ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentServiceImpl.java
  20. 0 1
      ruoyi-modules/yingpaipay-setting/src/main/java/com/yingpaipay/setting/controller/CarouselSettingController.java
  21. 2 1
      ruoyi-modules/yingpaipay-setting/src/main/java/com/yingpaipay/setting/service/impl/CarouselSettingServiceImpl.java

+ 1 - 1
ruoyi-admin/pom.xml

@@ -67,7 +67,7 @@
 
         <dependency>
             <groupId>com.yingpaipay</groupId>
-            <artifactId>yingpaipay-common-file</artifactId>
+            <artifactId>yingpaipay-common-document</artifactId>
         </dependency>
 
         <dependency>

+ 1 - 1
ruoyi-common/pom.xml

@@ -35,7 +35,7 @@
         <module>ruoyi-common-websocket</module>
         <module>ruoyi-common-sse</module>
         <module>yingpaipay-common-wechat</module>
-        <module>yingpaipay-common-file</module>
+        <module>yingpaipay-common-document</module>
     </modules>
 
     <artifactId>ruoyi-common</artifactId>

+ 1 - 1
ruoyi-common/ruoyi-common-bom/pom.xml

@@ -187,7 +187,7 @@
 
             <dependency>
                 <groupId>com.yingpaipay</groupId>
-                <artifactId>yingpaipay-common-file</artifactId>
+                <artifactId>yingpaipay-common-document</artifactId>
                 <version>${revision}</version>
             </dependency>
 

+ 1 - 1
ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java

@@ -193,7 +193,7 @@ public class GlobalExceptionHandler {
      */
     @ExceptionHandler(MethodArgumentNotValidException.class)
     public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
-        log.error(e.getMessage());
+        log.error("参数异常 : {}", e.getMessage());
         String message = StreamUtils.join(e.getBindingResult().getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
         return R.fail(message);
     }

+ 1 - 1
ruoyi-common/yingpaipay-common-file/pom.xml → ruoyi-common/yingpaipay-common-document/pom.xml

@@ -10,7 +10,7 @@
     </parent>
 
     <groupId>com.yingpaipay</groupId>
-    <artifactId>yingpaipay-common-file</artifactId>
+    <artifactId>yingpaipay-common-document</artifactId>
 
     <dependencies>
         <dependency>

+ 0 - 0
ruoyi-common/yingpaipay-common-file/src/main/java/com/yingpaipay/common/file/config/TextInConfig.java → ruoyi-common/yingpaipay-common-document/src/main/java/com/yingpaipay/common/file/config/TextInConfig.java


+ 96 - 0
ruoyi-common/yingpaipay-common-document/src/main/java/com/yingpaipay/common/file/util/PdfUtils.java

@@ -0,0 +1,96 @@
+package com.yingpaipay.common.file.util;
+
+import org.apache.pdfbox.io.MemoryUsageSetting;
+import org.apache.pdfbox.multipdf.PDFMergerUtility;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDPageContentStream;
+import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
+import org.apache.pdfbox.pdmodel.font.PDType1Font;
+
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+public class PdfUtils {
+
+    public static File watermark(File file, String remark)  throws IOException {
+        PDDocument document = PDDocument.load(file);
+        PDType1Font font = PDType1Font.HELVETICA_BOLD;
+        float fontSize = 10f;
+
+        for (PDPage page : document.getPages()) {
+//            PDRectangle pageSize = page.getMediaBox();
+            float x = 50;
+            float y = 30;
+
+            PDFontDescriptor fontDesc = font.getFontDescriptor();
+            float textWidth = font.getStringWidth(remark) / 1000 * fontSize;
+            float textHeight = fontDesc != null ? fontDesc.getAscent() / 1000 * fontSize : fontSize;
+            float descent = fontDesc != null ? fontDesc.getDescent() / 1000 * fontSize : 0;
+
+            float padding = 4f;
+            float rectX = x - padding;
+            float rectY = y + descent - padding;
+            float rectWidth = textWidth + 2 * padding;
+            float rectHeight = textHeight - descent + 2 * padding;
+
+            try (PDPageContentStream cs = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
+                Color color = new Color(255, 99, 99);
+                cs.setStrokingColor(color);
+                cs.setLineWidth(1f);
+                cs.addRect(rectX, rectY, rectWidth, rectHeight);
+                cs.stroke();
+
+                cs.beginText();
+                cs.setFont(font, fontSize);
+                cs.setNonStrokingColor(color);
+                cs.newLineAtOffset(x, y);
+                cs.showText(remark);
+                cs.endText();
+            }
+        }
+
+        File outputFile = File.createTempFile("watermarked_", ".pdf");
+        document.save(outputFile);
+        document.close();
+        return outputFile;
+    }
+
+    public static File merge(List<File> files, String fileName) throws IOException {
+        if (files == null || files.isEmpty()) {
+            throw new IllegalArgumentException("Base64 PDF list is empty or null");
+        }
+
+        // TODO 验证每个文件是否存在且可读
+        for (File file : files) {
+            if (file == null || !file.exists() || !file.isFile()) {
+                throw new IllegalArgumentException("Invalid PDF file: " + (file != null ? file.getAbsolutePath() : "null"));
+            }
+            if (!file.canRead()) {
+                throw new IllegalArgumentException("Cannot read PDF file: " + file.getAbsolutePath());
+            }
+        }
+
+        Path tempDir = Files.createTempDirectory("pdf_merge_");
+        Path outputPath = tempDir.resolve(fileName + ".pdf");
+
+        File outputFile = outputPath.toFile();
+
+        PDFMergerUtility merger = new PDFMergerUtility();
+        merger.setDestinationFileName(outputFile.getAbsolutePath());
+
+        for (File file : files) {
+            merger.addSource(file);
+        }
+
+        merger.mergeDocuments(MemoryUsageSetting.setupMainMemoryOnly());
+
+        return outputFile;
+
+    }
+
+}

+ 0 - 0
ruoyi-common/yingpaipay-common-file/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports → ruoyi-common/yingpaipay-common-document/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports


+ 0 - 157
ruoyi-common/yingpaipay-common-file/src/main/java/com/yingpaipay/common/file/util/WatermarkUtils.java

@@ -1,157 +0,0 @@
-package com.yingpaipay.common.file.util;
-
-import org.apache.pdfbox.pdmodel.PDDocument;
-import org.apache.pdfbox.pdmodel.PDPage;
-import org.apache.pdfbox.pdmodel.PDPageContentStream;
-import org.apache.pdfbox.pdmodel.common.PDRectangle;
-import org.apache.pdfbox.pdmodel.font.PDFontDescriptor;
-import org.apache.pdfbox.pdmodel.font.PDType1Font;
-
-import org.docx4j.jaxb.Context;
-import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
-import org.docx4j.openpackaging.parts.WordprocessingML.FooterPart;
-import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
-import org.docx4j.relationships.Relationship;
-import org.docx4j.wml.*;
-
-import javax.imageio.ImageIO;
-import java.awt.*;
-import java.awt.Color;
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-
-public class WatermarkUtils {
-
-    public static File processFile(File tempFile, String remark) {
-        String fileName = tempFile.getName().toLowerCase();
-
-        try {
-            if (fileName.endsWith(".pdf")) {
-                return addPdfFooterWatermark(tempFile, remark);
-            } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png")) {
-                return addImageFooterWatermark(tempFile, remark);
-            } else if (fileName.endsWith(".docx")) {
-                return addDocxFooterWatermark(tempFile, remark);
-            } else {
-                return tempFile;
-            }
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private static File addPdfFooterWatermark(File inputFile, String watermark) throws IOException {
-        PDDocument document = PDDocument.load(inputFile);
-        PDType1Font font = PDType1Font.HELVETICA_BOLD;
-        float fontSize = 10f;
-
-        for (PDPage page : document.getPages()) {
-//            PDRectangle pageSize = page.getMediaBox();
-            float x = 50;
-            float y = 30;
-
-            PDFontDescriptor fontDesc = font.getFontDescriptor();
-            float textWidth = font.getStringWidth(watermark) / 1000 * fontSize;
-            float textHeight = fontDesc != null ? fontDesc.getAscent() / 1000 * fontSize : fontSize;
-            float descent = fontDesc != null ? fontDesc.getDescent() / 1000 * fontSize : 0;
-
-            float padding = 4f;
-            float rectX = x - padding;
-            float rectY = y + descent - padding;
-            float rectWidth = textWidth + 2 * padding;
-            float rectHeight = textHeight - descent + 2 * padding;
-
-            try (PDPageContentStream cs = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true, true)) {
-                Color color = new Color(255, 99, 99);
-                cs.setStrokingColor(color);
-                cs.setLineWidth(1f);
-                cs.addRect(rectX, rectY, rectWidth, rectHeight);
-                cs.stroke();
-
-                cs.beginText();
-                cs.setFont(font, fontSize);
-                cs.setNonStrokingColor(color);
-                cs.newLineAtOffset(x, y);
-                cs.showText(watermark);
-                cs.endText();
-            }
-        }
-
-        File outputFile = File.createTempFile("watermarked_", ".pdf");
-        document.save(outputFile);
-        document.close();
-        return outputFile;
-    }
-
-    // ===== 图片水印 =====
-    private static File addImageFooterWatermark(File inputFile, String watermark) throws IOException {
-        BufferedImage image = ImageIO.read(inputFile);
-        Graphics2D g2d = image.createGraphics();
-        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-        g2d.setColor(new Color(128, 128, 128, 180)); // 半透明灰色
-        g2d.setFont(new Font("SansSerif", Font.BOLD, 16));
-
-        g2d.getFontMetrics();
-        int x = 20;
-        int y = image.getHeight() - 10; // 底部留一点空间
-
-        g2d.drawString(watermark, x, y);
-        g2d.dispose();
-
-        String ext = getFileExtension(inputFile.getName());
-        File outputFile = File.createTempFile("watermarked_", "." + ext);
-        ImageIO.write(image, ext, outputFile);
-        return outputFile;
-    }
-
-    // ===== Word (.docx) 页脚水印 =====
-    private static File addDocxFooterWatermark(File inputFile, String watermark) throws Exception {
-        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(inputFile);
-        MainDocumentPart documentPart = wordMLPackage.getMainDocumentPart();
-
-        Document document = documentPart.getJaxbElement();
-        Body body = document.getBody();
-        if (body == null) {
-            body = Context.getWmlObjectFactory().createBody();
-            document.setBody(body);
-        }
-
-        SectPr sectPr = body.getSectPr();
-        if (sectPr == null) {
-            sectPr = Context.getWmlObjectFactory().createSectPr();
-            body.setSectPr(sectPr);
-        }
-
-        // 创建页脚内容
-        FooterPart footerPart = new FooterPart();
-        Ftr ftr = Context.getWmlObjectFactory().createFtr();
-
-        P paragraph = Context.getWmlObjectFactory().createP();
-        R run = Context.getWmlObjectFactory().createR();
-        Text text = Context.getWmlObjectFactory().createText();
-        text.setValue(watermark);
-        run.getContent().add(text);
-        paragraph.getContent().add(run);
-        ftr.getContent().add(paragraph);
-
-        footerPart.setJaxbElement(ftr);
-
-        // 关联页脚到文档
-        Relationship rel = documentPart.addTargetPart(footerPart);
-        FooterReference footerRef = Context.getWmlObjectFactory().createFooterReference();
-        footerRef.setId(rel.getId());
-        footerRef.setType(HdrFtrRef.DEFAULT);
-        sectPr.getEGHdrFtrReferences().add(footerRef);
-
-        File outputFile = File.createTempFile("watermarked_", ".docx");
-        wordMLPackage.save(outputFile);
-        return outputFile;
-    }
-
-    // ===== 工具方法 =====
-    private static String getFileExtension(String name) {
-        int lastDot = name.lastIndexOf('.');
-        return (lastDot > 0) ? name.substring(lastDot + 1).toLowerCase() : "jpg";
-    }
-}

+ 1 - 1
ruoyi-modules/yingpaipay-business/pom.xml

@@ -105,7 +105,7 @@
 
         <dependency>
             <groupId>com.yingpaipay</groupId>
-            <artifactId>yingpaipay-common-file</artifactId>
+            <artifactId>yingpaipay-common-document</artifactId>
         </dependency>
     </dependencies>
 

+ 18 - 8
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/controller/applet/AppletScanController.java

@@ -1,32 +1,42 @@
 package com.yingpaipay.business.controller.applet;
 
+import com.yingpaipay.business.domain.bo.AppletDocumentScanSubmitBo;
 import com.yingpaipay.business.domain.bo.AppletScanBo;
+import com.yingpaipay.business.domain.bo.AppletUploadOnSubmitBo;
+import com.yingpaipay.business.domain.vo.AppletDocumentScanSubmitVo;
 import com.yingpaipay.business.domain.vo.AppletScanVo;
+import com.yingpaipay.business.service.IDocumentService;
 import com.yingpaipay.business.service.ITextInService;
 import lombok.RequiredArgsConstructor;
 import org.dromara.common.core.domain.R;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.web.core.BaseController;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
 
+@Validated
 @RestController
 @RequestMapping("/applet/scan")
 @RequiredArgsConstructor
 public class AppletScanController extends BaseController {
 
     private final ITextInService textInService;
+    private final IDocumentService documentService;
 
     @PostMapping("/scan")
     public R<AppletScanVo> scan(@RequestBody AppletScanBo bo) {
         return R.ok(new AppletScanVo(textInService.wordToPdf(textInService.imageToWord(bo.getFile()))));
     }
 
+    @GetMapping("/listToSubmit")
+    public TableDataInfo<AppletDocumentScanSubmitVo> listToSubmit(AppletDocumentScanSubmitBo bo, PageQuery pageQuery) {
+        return documentService.listToSubmit(bo, pageQuery);
+    }
 
-//    @PostMapping("/upload")
-//    public R upload(@RequestBody AppletUploadBo bo) {
-//
-//    }
+    @PostMapping("/uploadOnSubmit")
+    public R<Void> uploadOnSubmit(@Validated @RequestBody AppletUploadOnSubmitBo bo) {
+        return toAjax(documentService.uploadOnSubmit(bo));
+    }
 
 }

+ 10 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/AppletDocumentScanSubmitBo.java

@@ -0,0 +1,10 @@
+package com.yingpaipay.business.domain.bo;
+
+import lombok.Data;
+
+@Data
+public class AppletDocumentScanSubmitBo {
+
+    private String name;
+
+}

+ 18 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/bo/AppletUploadOnSubmitBo.java

@@ -0,0 +1,18 @@
+package com.yingpaipay.business.domain.bo;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class AppletUploadOnSubmitBo {
+
+    @NotNull
+    private Long documentId;
+
+    @NotEmpty
+    private List<String> fileBase64List;
+
+}

+ 32 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/AppletDocumentScanSubmitVo.java

@@ -0,0 +1,32 @@
+package com.yingpaipay.business.domain.vo;
+
+import lombok.Data;
+import org.dromara.common.translation.annotation.Translation;
+import org.dromara.common.translation.constant.TransConstant;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class AppletDocumentScanSubmitVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    private Long id;
+
+    private String name;
+
+    private String folder;
+
+    private String project;
+
+    @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "createBy")
+    private Long createBy;
+
+    private Date createTime;
+
+    private Date deadline;
+
+}

+ 3 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/domain/vo/DocumentAuditLogVo.java

@@ -3,6 +3,8 @@ package com.yingpaipay.business.domain.vo;
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
 import com.yingpaipay.business.constant.DictTypeConst;
+import com.yingpaipay.business.domain.DocumentAuditLog;
+import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
 import org.dromara.common.excel.annotation.ExcelDictFormat;
 import org.dromara.common.translation.annotation.Translation;
@@ -14,6 +16,7 @@ import java.util.Date;
 
 @Data
 @ExcelIgnoreUnannotated
+@AutoMapper(target = DocumentAuditLog.class)
 public class DocumentAuditLogVo implements Serializable {
 
     @Serial

+ 4 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/IDocumentService.java

@@ -97,4 +97,8 @@ public interface IDocumentService {
     TableDataInfo<AppletRecentDocumentVo> listRecentOnApplet(AppletRecentDocumentBo bo, PageQuery pageQuery);
 
     TableDataInfo<AppletMyTaskDocumentVo> listDocument(AppletMyTaskDocumentBo bo, PageQuery pageQuery);
+
+    TableDataInfo<AppletDocumentScanSubmitVo> listToSubmit(AppletDocumentScanSubmitBo bo, PageQuery pageQuery);
+
+    boolean uploadOnSubmit(AppletUploadOnSubmitBo bo);
 }

+ 10 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/CommonFolderService.java

@@ -144,4 +144,14 @@ public class CommonFolderService {
         }
         return baseMapper.selectByIds(ids);
     }
+
+    public List<Folder> queryByProjectIds(List<Long> projectIds) {
+        if (projectIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return baseMapper.selectList(
+            Wrappers.lambdaQuery(Folder.class)
+                .in(Folder::getProjectId, projectIds)
+        );
+    }
 }

+ 11 - 0
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/CommonProjectService.java

@@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 @Service
@@ -72,4 +73,14 @@ public class CommonProjectService {
         baseMapper.selectList(wrapper).forEach(e -> ids.add(e.getId()));
         return ids;
     }
+
+    public List<Project> queryByIds(List<Long> ids) {
+        if (ids.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return baseMapper.selectList(
+            Wrappers.lambdaQuery(Project.class)
+                .in(Project::getId, ids)
+        );
+    }
 }

+ 101 - 8
ruoyi-modules/yingpaipay-business/src/main/java/com/yingpaipay/business/service/impl/DocumentServiceImpl.java

@@ -1,26 +1,22 @@
 package com.yingpaipay.business.service.impl;
 
-import cn.hutool.core.util.ObjectUtil;
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.yingpaipay.business.constant.DictTypeConst;
 import com.yingpaipay.business.constant.DocumentAuditorTypeConst;
 import com.yingpaipay.business.constant.DocumentStatusConst;
-import com.yingpaipay.business.constant.DocumentTypeConst;
 import com.yingpaipay.business.domain.DocumentAuditLog;
 import com.yingpaipay.business.domain.Folder;
 import com.yingpaipay.business.domain.Project;
 import com.yingpaipay.business.domain.bo.*;
 import com.yingpaipay.business.domain.vo.*;
 import com.yingpaipay.business.mapper.DocumentAuditLogMapper;
-import com.yingpaipay.common.file.util.WatermarkUtils;
+import com.yingpaipay.common.file.util.PdfUtils;
 import jakarta.servlet.http.HttpServletResponse;
 import org.apache.commons.io.FilenameUtils;
 import org.dromara.common.core.exception.BusinessException;
 import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.MessageUtils;
-import org.dromara.common.core.utils.SpringUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.core.utils.file.FileUtils;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -35,7 +31,6 @@ import org.dromara.common.oss.factory.OssFactory;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.common.tenant.helper.TenantHelper;
 import org.dromara.system.domain.vo.SysOssVo;
-import org.dromara.system.service.ISysDictDataService;
 import org.dromara.system.service.ISysDictTypeService;
 import org.dromara.system.service.ISysOssService;
 import org.dromara.system.service.ISysUserService;
@@ -47,6 +42,9 @@ import com.yingpaipay.business.service.IDocumentService;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.*;
 
 /**
@@ -385,7 +383,7 @@ public class DocumentServiceImpl implements IDocumentService {
                 storage.download(sysOss.getFileName(), fos, null);
             }
 
-            File processedFile = WatermarkUtils.processFile(tempFile, buildRemark(ossId));
+            File processedFile = PdfUtils.watermark(tempFile, buildRemark(ossId));
 
             FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
             response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
@@ -563,6 +561,101 @@ public class DocumentServiceImpl implements IDocumentService {
         }));
     }
 
+    @Override
+    public TableDataInfo<AppletDocumentScanSubmitVo> listToSubmit(AppletDocumentScanSubmitBo bo, PageQuery pageQuery) {
+        IPage<Document> page = baseMapper.selectPage(
+            pageQuery.build(),
+            Wrappers.lambdaQuery(Document.class)
+                .eq(Document::getSubmitterId, LoginHelper.getUserId())
+                .eq(Document::getStatus, List.of(DocumentStatusConst.UN_UPLOAD, DocumentStatusConst.AUDIT_REJECT))
+                .like(StringUtils.isNotBlank(bo.getName()), Document::getName, bo.getName())
+                .orderByDesc(Document::getId)
+        );
+        List<Long> projectIds = new ArrayList<>();
+        Map<Long, Folder> folderMap = new HashMap<>();
+        Map<Long, String> projectMap = new HashMap<>();
+
+        page.getRecords().forEach(e -> projectIds.add(e.getProjectId()));
+        projectService.queryByIds(projectIds).forEach(e -> projectMap.put(e.getId(), e.getName()));
+        List<Folder> folderList = folderService.queryByProjectIds(projectIds);
+        folderList.forEach(e -> folderMap.put(e.getId(), e));
+
+        return TableDataInfo.build(page.convert(e -> {
+            AppletDocumentScanSubmitVo vo = new AppletDocumentScanSubmitVo();
+            vo.setId(e.getId());
+            vo.setName(e.getName());
+
+            // 显示全路径
+            Folder currentFolder = folderMap.get(e.getFolderId());
+            String folder = "";
+            while (currentFolder != null) {
+                folder = "/" + currentFolder.getName() + folder;
+                currentFolder = folderMap.get(currentFolder.getParentId());
+            }
+            vo.setFolder(folder);
+
+            vo.setProject(projectMap.get(e.getProjectId()));
+            vo.setCreateBy(e.getCreateBy());
+            vo.setCreateTime(e.getCreateTime());
+            vo.setDeadline(e.getSubmitDeadline());
+            return vo;
+        }));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean uploadOnSubmit(AppletUploadOnSubmitBo bo) {
+
+        Document document = baseMapper.selectById(bo.getDocumentId());
+
+        List<String> fileBase64List = bo.getFileBase64List();
+
+        if (fileBase64List == null || fileBase64List.isEmpty()) {
+            throw new BusinessException("提交的文件列表为空");
+        }
+
+        List<File> files = new ArrayList<>();
+
+        File finalFile;
+        try {
+
+            for (int i = 0; i < fileBase64List.size(); i++) {
+                String base64Str = fileBase64List.get(i);
+                if (base64Str == null || base64Str.trim().isEmpty()) {
+                    continue;
+                }
+
+                String cleanBase64 = base64Str;
+                if (base64Str.contains(",")) {
+                    cleanBase64 = base64Str.split(",", 2)[1];
+                }
+
+                byte[] pdfBytes = Base64.getDecoder().decode(cleanBase64);
+
+                Path tempPath = Files.createTempFile("document_", "_" + i + ".pdf");
+                Files.write(tempPath, pdfBytes);
+                files.add(tempPath.toFile());
+            }
+
+            if (files.isEmpty()) {
+                throw new BusinessException("未解析到有效的 PDF 文件数据");
+            }
+
+            finalFile = PdfUtils.merge(files, document.getName());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        SysOssVo ossVo = ossService.upload(finalFile);
+
+        return baseMapper.update(
+            Wrappers.lambdaUpdate(Document.class)
+                .eq(Document::getId, bo.getDocumentId())
+                .set(Document::getOssId, ossVo.getOssId())
+                .set(Document::getStatus, DocumentStatusConst.UN_AUDIT)
+                .set(Document::getSubmitTime, new Date())
+        ) > 0;
+    }
+
     private LambdaQueryWrapper<Document> buildFilingListWrapper(TaskCenterFilingListBo bo, List<Long> folderIds) {
         return Wrappers.lambdaQuery(Document.class)
             .like(StringUtils.isNotBlank(bo.getName()), Document::getName, bo.getName())
@@ -607,7 +700,7 @@ public class DocumentServiceImpl implements IDocumentService {
     private DocumentAuditLog buildLog(Document document, String type, DocumentAuditBo bo) {
         DocumentAuditLog log = new DocumentAuditLog();
         log.setDocumentId(document.getId());
-        log.setOssId(bo.getOssId());
+        log.setOssId(bo.getOssId() != null ? bo.getOssId() : document.getOssId());
         log.setAuditorType(type);
         log.setAuditorId(LoginHelper.getUserId());
         log.setResult(bo.getResult());

+ 0 - 1
ruoyi-modules/yingpaipay-setting/src/main/java/com/yingpaipay/setting/controller/CarouselSettingController.java

@@ -1,6 +1,5 @@
 package com.yingpaipay.setting.controller;
 
-import java.util.ArrayList;
 import java.util.List;
 
 import lombok.RequiredArgsConstructor;

+ 2 - 1
ruoyi-modules/yingpaipay-setting/src/main/java/com/yingpaipay/setting/service/impl/CarouselSettingServiceImpl.java

@@ -77,7 +77,8 @@ public class CarouselSettingServiceImpl implements ICarouselSettingService {
     private LambdaQueryWrapper<CarouselSetting> buildQueryWrapper(CarouselSettingBo bo) {
         Map<String, Object> params = bo.getParams();
         LambdaQueryWrapper<CarouselSetting> lqw = Wrappers.lambdaQuery();
-        lqw.orderByAsc(CarouselSetting::getId);
+        // TODO 需要按排序从小到大
+        lqw.orderByAsc(CarouselSetting::getSort);
         return lqw;
     }