Kaynağa Gözat

日志保存优化

cr 2 gün önce
ebeveyn
işleme
425a210a0b

+ 1 - 1
blade-service/blade-business/src/main/java/org/springblade/business/feignClient/ContractLogClientImpl.java

@@ -211,7 +211,7 @@ public class ContractLogClientImpl implements ContractLogClient {
 
     @Override
     public List<ContractLog> queryContractLogByIds1(String theLogIds) {
-        List<Long> ids = Func.toLongList(theLogIds);
+        Func.toLongList(theLogIds)
         return this.contractLogService.list(Wrappers.<ContractLog>lambdaQuery().in(ContractLog::getId, ids));
     }
 

+ 736 - 258
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/ExcelTabController.java

@@ -7,6 +7,7 @@ import cn.hutool.log.StaticLog;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.alibaba.nacos.shaded.com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -95,6 +96,7 @@ import org.springframework.web.context.request.RequestContextHolder;
 import org.springframework.web.context.request.ServletRequestAttributes;
 import org.springframework.web.multipart.MultipartFile;
 
+
 import javax.annotation.Resource;
 import javax.imageio.ImageIO;
 import javax.servlet.http.HttpServletRequest;
@@ -115,9 +117,7 @@ import java.sql.SQLException;
 import java.text.SimpleDateFormat;
 import java.util.List;
 import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -2818,7 +2818,307 @@ public class ExcelTabController extends BladeController {
         toClient.close();
     }
 
-    /**
+//    /**
+//     * 日志预览
+//     */
+//    @GetMapping("/get-the-log-pdfInfo")
+//    @ApiOperationSupport(order = 25)
+//    @ApiOperation(value = "日志预览")
+//    @ApiImplicitParams({
+//            @ApiImplicitParam(name = "nodePrimaryKeyId", value = "当前操作的日志类型ID,即左侧列表的节点primaryKeyId"),
+//            @ApiImplicitParam(name = "String pkeyId", value = "当前表pKeyId"),
+//            @ApiImplicitParam(name = "recordTime", value = "当前选择的填写日期,即右侧日期控件所选日期,格式为 yyyy-MM-dd")
+//    })
+//    public R<String> getTheLogPdInfo(String pkeyId, String nodePrimaryKeyId, String recordTime, String contractId,Long createUser) throws Exception {
+//        //获取配置的路径
+//        String file_path = FileUtils.getSysLocalFileUrl();
+//        if (StringUtils.isEmpty(recordTime)) {
+//            recordTime = DateUtil.format(DateUtil.now(), "yyyy-MM-dd");
+//        }
+//        List<ContractLog> contractLogList = this.contractLogClient.queryContractLogByPrimaryKeyIdAndRecordTimeList(nodePrimaryKeyId, recordTime, contractId,createUser);
+//        List<String> ids = contractLogList.stream().map(ContractLog::getId).map(String::valueOf).collect(Collectors.toList());
+//        if (ids.size() > 0) {
+//            //PDF路径
+//            List<String> pdfUrls = new ArrayList<>();
+//
+//            for (String dataId : ids) {
+//                //查询对应的html
+//                WbsTreePrivate tableNode = this.wbsTreePrivateService.getOne(Wrappers.<WbsTreePrivate>lambdaQuery().eq(WbsTreePrivate::getPKeyId, pkeyId));
+//
+//                if (tableNode == null) {
+//                    return R.fail("该数据下无此节点!");
+//                }
+//
+//                String htmlString = this.getHtmlString(pkeyId);
+//
+//                if (StringUtils.isEmpty(tableNode.getHtmlUrl()) || StringUtils.isEmpty(htmlString)) {
+//                    return R.fail("未获取到元素表信息!");
+//                }
+//
+//                // 获取清表信息
+//                ExcelTab excelTab = excelTabService.getById(tableNode.getExcelId());
+//                if (excelTab == null) {
+//                    return R.fail("失败");
+//                }
+//
+//                //获取数据
+//                List<Map<String, Object>> businessDataMapList = this.getTheLogBusinessData(dataId, nodePrimaryKeyId, recordTime, contractId).getData();
+//                ProjectInfo projectInfo = projectInfoService.getById(tableNode.getProjectId());
+//                //处理数据
+//                for (Map<String, Object> dataMap : businessDataMapList) {
+//                    // 获取excel流 和 html流
+//                    Workbook wb = new Workbook();
+//                    wb.loadFromMHtml(CommonUtil.getOSSInputStream(excelTab.getFileUrl()));
+//                    //获取工作表
+//                    Worksheet sheet = wb.getWorksheets().get(0);
+//                    Document doc = Jsoup.parse(htmlString);
+//                    Element table = doc.select("table").first();
+//                    Elements trs = table.select("tr");
+//
+//                    //添加标题
+//                    String sys_isonline = ParamCache.getValue(CommonConstant.SYS_ISONLINE);
+//                    if ("20".equals(sys_isonline)) { //甬台温
+//                        CellRange[] columns = sheet.getMergedCells();
+//                        for (int i = 0; i < columns.length; i++) {
+//                            CellRange cellRange = columns[i];
+//                            if (cellRange.getStyle().getFont().getSize() >= 12 && (Func.isNotEmpty(cellRange.getValue2()) || Func.isNotEmpty(cellRange.getValue()))) {
+//                                String title = projectInfo.getProjectName();
+//                                if (title.length() >= 30) {
+//                                    cellRange.setRowHeight(40);
+//                                    cellRange.getStyle().setWrapText(true);
+//                                }
+//                                cellRange.getStyle().getFont().setSize(18);
+//                                cellRange.getStyle().setHorizontalAlignment(HorizontalAlignType.Center);
+//                                cellRange.setText(projectInfo.getProjectName());
+//                                break;
+//                            }
+//                        }
+//                    } else {
+//                        for (int i = 1; i < 6; i++) {
+//                            Element tr = trs.get(i);
+//                            Elements tds = tr.select("td");
+//                            for (int j = 0; j < tds.size(); j++) {
+//                                Element data = tds.get(j);
+//                                String style = data.attr("style");
+//                                if (style.contains("font-size")) {
+//                                    int fontsize = Integer.parseInt(style.substring(style.indexOf("font-size:") + 10, style.indexOf(".0pt")));
+//                                    if (StringUtils.isNotEmpty(data.text()) && fontsize >= 12) {
+//                                        Element element = trs.get(i - 1).select("td").get(0);
+//                                        String textainfo = element.text();
+//                                        if (textainfo == null || textainfo == "" || Func.isEmpty(textainfo)) {
+//                                            int x1, y1;
+//                                            if ((element.html().indexOf("x1") >= 0 && element.html().indexOf("y1") >= 0) || (element.hasAttr("x1") && element.hasAttr("y1"))) {
+//                                                if (element.html().indexOf("el-tooltip") >= 0) {
+//                                                    x1 = Integer.parseInt(element.children().get(0).children().get(0).attr("x1"));
+//                                                    y1 = Integer.parseInt(element.children().get(0).children().get(0).attr("y1"));
+//                                                } else {
+//                                                    Elements children = element.children();
+//                                                    if (children.size() >= 1) {
+//                                                        x1 = Integer.parseInt(element.children().get(0).attr("x1"));
+//                                                        y1 = Integer.parseInt(element.children().get(0).attr("y1"));
+//                                                    } else {
+//                                                        x1 = Integer.parseInt(element.attr("x1"));
+//                                                        y1 = Integer.parseInt(element.attr("y1"));
+//                                                    }
+//                                                }
+//                                                if (x1 == 0) {
+//                                                    x1 = 1;
+//                                                }
+//                                                final CellRange cellRange = sheet.getCellRange(y1, x1);
+//                                                cellRange.setText(projectInfo.getProjectName());
+//                                            }
+//                                            break;
+//                                        }
+//                                    }
+//                                }
+//                            }
+//                        }
+//                    }
+//
+//                    if (ObjectUtil.isNotEmpty(dataMap)) {
+//                        for (String val : dataMap.keySet()) {
+//                            boolean flag = false;
+//                            if (val.indexOf("__") >= 0) {
+//                                String[] DataVal = val.split("__");
+//                                String[] xy = DataVal[1].split("_");
+//                                if (trs.size() > Integer.parseInt(xy[0])) {
+//                                    Element trData = trs.get(Integer.parseInt(xy[0]));
+//                                    Elements tdDatas = trData.select("td");
+//                                    if (tdDatas.size() > Integer.parseInt(xy[1])) {
+//                                        Element data = tdDatas.get(Integer.parseInt(xy[1]));
+//                                        if (data.html().indexOf("date") >= 0) {
+//                                            flag = true;
+//                                        }
+//                                        if (data.html().indexOf("x1") >= 0 && data.html().indexOf("y1") >= 0) {
+//                                            int x1, y1;
+//
+//                                            if (data.html().indexOf("el-tooltip") >= 0) {
+//                                                x1 = Integer.parseInt(data.children().get(0).children().get(0).attr("x1"));
+//                                                y1 = Integer.parseInt(data.children().get(0).children().get(0).attr("y1"));
+//                                            } else {
+//                                                x1 = Integer.parseInt(data.children().get(0).attr("x1"));
+//                                                y1 = Integer.parseInt(data.children().get(0).attr("y1"));
+//                                            }
+//                                            if (x1 == 0) {
+//                                                x1 = 1;
+//                                            }
+//                                            String myData = dataMap.get(val) + "";
+//                                            if (((myData.indexOf("T") >= 0 && myData.indexOf("-") >= 0) || (myData.indexOf(",") >= 0 && myData.indexOf("]") >= 0)) && flag) {
+//                                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+//                                                sdf.setTimeZone(TimeZone.getTimeZone("GTM+8"));
+//                                                SimpleDateFormat formatStr = new SimpleDateFormat("yyyy年MM月dd日");
+//                                                if (myData.indexOf(",") >= 0 && myData.indexOf("]") >= 0) {
+//                                                    myData = myData.replace("[", "").replace("]", "").replaceAll("'", "");
+//                                                    String[] dataVal = myData.split(",");
+//                                                    Date Start_dataStr = sdf.parse(dataVal[0]);
+//                                                    Date end_dataStr = sdf.parse(dataVal[1]);
+//                                                    String StartDate = formatStr.format(Start_dataStr);
+//                                                    String endDate = formatStr.format(end_dataStr);
+//                                                    if (StartDate.equals(endDate)) {
+//                                                        myData = StartDate;
+//                                                    } else {
+//                                                        myData = StartDate + "-" + endDate;
+//                                                    }
+//                                                } else {
+//                                                    String[] dataStr = myData.split("T")[0].split("-");
+//                                                    if (dataStr.length == 3) {
+//                                                        myData = StringUtil.format("{}年{}月{}日", dataStr[0], dataStr[1], Integer.parseInt(dataStr[2]));
+//                                                    }
+//                                                }
+//                                            }
+//                                            if (myData.indexOf("lang.String") >= 0) {
+//                                                Object obj = dataMap.get(val);
+//                                                if (obj instanceof String[]) {
+//                                                    String[] dataDate = (String[]) obj;
+//                                                    String HtmlEle = data.toString();
+//                                                    if (HtmlEle.indexOf("el-date-picker") >= 0) {//时间时间段处理格式
+//                                                        if ((dataDate[0].trim()).equals((dataDate[1].trim()))) {
+//                                                            myData = dataDate[0];
+//                                                        } else {
+//                                                            myData = dataDate[0].replace("\"", "") + "-" + dataDate[1].trim().replace("\"", "");
+//                                                        }
+//                                                    } else {
+//                                                        myData = Func.convert(dataDate, String.class).replaceAll(" ", "").replaceAll(",","、");
+//                                                        if (myData.startsWith("[") && myData.endsWith("]")) {
+//                                                            // 去掉两端的中括号
+//                                                            myData=myData.replaceAll("\\[","").replaceAll("]","");
+//                                                        }
+//                                                    }
+//
+//                                                }
+//                                            }
+//
+//                                            if (myData.indexOf("http") >= 0 && (myData.indexOf("aliyuncs") >= 0 || myData.indexOf("183.247.216.148") >= 0)) {
+//                                                Element element = trs.get(y1).select("td").get(x1);
+//                                                String[] styles = element.attr("style").split(";");
+//                                                int Height = 0;
+//                                                for (String sty : styles) {
+//                                                    if (sty.indexOf("height:") >= 0) {
+//                                                        Height = Integer.parseInt(sty.replace("height:", "").replace("px", ""));
+//                                                    }
+//                                                }
+//
+//                                                BufferedImage image = ImageIO.read(CommonUtil.getOSSInputStream(myData));
+//                                                ExcelPicture pic = sheet.getPictures().add(y1, x1, image);
+//                                                pic.setHeight(Height);
+//                                                sheet.getCellRange(y1, x1).getStyle().setShrinkToFit(true);
+//
+//                                            } else if (data.html().indexOf("hc-form-checkbox-group") >= 0) {
+//                                                CellRange cellRange = sheet.getCellRange(y1, x1);
+//                                                String exceVal = cellRange.getValue().replaceAll(" ", "");
+//                                                //如果有□ 代表 自动生成  如果没有 代表后期添加 需要显示html 中的值
+//                                                if (exceVal.indexOf("□") >= 0) {
+//                                                    if (myData.equals("1")) {
+//                                                        cellRange.setValue(exceVal.replace("□", "\u2611"));
+//                                                    }
+//                                                } else {
+//                                                    List<Node> nodes = data.childNodes();
+//                                                    Node node = nodes.get(nodes.size() - 1);
+//                                                    String dataJson = node.attr(":objs");
+//                                                    if (StringUtils.isNotEmpty(dataJson)) {
+//                                                        JSONArray jsonArray = JSONArray.parseArray(dataJson);
+//                                                        List<Integer> idList = Func.toIntList(myData);
+//                                                        int indexx = 0;
+//                                                        if (idList.get(0) >= 1) {
+//                                                            indexx = idList.get(0) - 1;
+//                                                        }
+//
+//                                                        String dataInfo = jsonArray.getJSONObject(indexx).getString("name");
+//                                                        for (int inx = 1; inx < idList.size(); inx++) {
+//                                                            int valIndex = idList.get(inx) - 1;
+//                                                            dataInfo = dataInfo + "," + jsonArray.getJSONObject(valIndex).getString("name");
+//                                                        }
+//                                                        cellRange.setValue(dataInfo);
+//                                                    }
+//
+//                                                }
+//                                            } else {
+//                                                final CellRange cellRange = sheet.getCellRange(y1, x1);
+//                                                cellRange.setText(myData);
+//                                            }
+//                                        }
+//                                    }
+//                                }
+//                            }
+//                        }
+//                    }
+//                    // 组装电签设置
+//                    Elements dqids = table.getElementsByAttribute("dqid");
+//                    for (Element element : dqids) {
+//                        String dqid = element.attr("dqid");
+//                        Elements x11 = element.getElementsByAttribute("x1");
+//                        if (x11 != null && x11.size() >= 1) {
+//                            Element element1 = x11.get(x11.size() - 1);
+//                            int x1 = Func.toInt(element1.attr("x1"));
+//                            int y1 = Func.toInt(element1.attr("y1"));
+//
+//                            CellRange cellRange = sheet.getCellRange(y1, x1);
+//                            if (cellRange != null) {
+//                                // 创建字体
+//                                String text = cellRange.getText();
+//                                if (StringUtil.hasText(text)) {
+//                                    dqid = text + "*" + dqid;
+//                                }
+//                                cellRange.setText(dqid);
+//                                cellRange.getCellStyle().getExcelFont().setSize(1);
+//                                cellRange.getCellStyle().getExcelFont().setColor(Color.WHITE);
+//                            }
+//                        }
+//                    }
+//
+//                    Long fileName = SnowFlakeUtil.getId();
+//                    String onePdfPath = file_path + "/pdf//" + fileName + ".pdf";
+//                    sheet.saveToPdf(onePdfPath);
+//                    BladeFile bladeFile = this.newIOSSClient.uploadFile(fileName + ".pdf", onePdfPath);
+//                    pdfUrls.add(bladeFile.getLink());
+//                    wb.dispose();
+//                }
+//            }
+//            if (pdfUrls.size() > 0) {
+//                try {
+//                    String mergePdfPath = file_path + "/pdf//" + SnowFlakeUtil.getId() + ".pdf";
+//                    File oldMergePdf = ResourceUtil.getFile(mergePdfPath);
+//                    if (oldMergePdf.exists()) {
+//                        oldMergePdf.delete();
+//                    }
+//                    FileUtils.mergePdfPublicMethods(pdfUrls, mergePdfPath);
+//                    BladeFile mergeFile = this.newIOSSClient.uploadFile(SnowFlakeUtil.getId() + new Date().getTime() + ".pdf", mergePdfPath);
+//
+//                    //修改记录,当天的日志所有表的合并pdf都一样,即ids都修改成一样的
+//                    this.contractLogClient.updateTheLogPdfUrlByIds(StringUtils.join(ids, ","), mergeFile.getLink());
+//
+//                    return R.data(mergeFile.getLink());
+//                } catch (Exception e) {
+//                    e.printStackTrace();
+//                    return R.fail("数据异常");
+//                }
+//            }
+//        }
+//
+//        return R.data(null);
+//    }
+
+        /**
      * 日志预览
      */
     @GetMapping("/get-the-log-pdfInfo")
@@ -2829,293 +3129,471 @@ public class ExcelTabController extends BladeController {
             @ApiImplicitParam(name = "String pkeyId", value = "当前表pKeyId"),
             @ApiImplicitParam(name = "recordTime", value = "当前选择的填写日期,即右侧日期控件所选日期,格式为 yyyy-MM-dd")
     })
-    public R<String> getTheLogPdInfo(String pkeyId, String nodePrimaryKeyId, String recordTime, String contractId,Long createUser) throws Exception {
-        //获取配置的路径
+    public R<String> getTheLogPdInfo(
+            String pkeyId, String nodePrimaryKeyId, String recordTime, String contractId, Long createUser) throws Exception {
+        // ========================= 1. 前置参数校验+初始化(仅1次)=========================
         String file_path = FileUtils.getSysLocalFileUrl();
-        if (StringUtils.isEmpty(recordTime)) {
+        if (StrUtil.isEmpty(recordTime)) {
             recordTime = DateUtil.format(DateUtil.now(), "yyyy-MM-dd");
         }
-        List<ContractLog> contractLogList = this.contractLogClient.queryContractLogByPrimaryKeyIdAndRecordTimeList(nodePrimaryKeyId, recordTime, contractId,createUser);
-        List<String> ids = contractLogList.stream().map(ContractLog::getId).map(String::valueOf).collect(Collectors.toList());
-        if (ids.size() > 0) {
-            //PDF路径
-            List<String> pdfUrls = new ArrayList<>();
 
-            for (String dataId : ids) {
-                //查询对应的html
-                WbsTreePrivate tableNode = this.wbsTreePrivateService.getOne(Wrappers.<WbsTreePrivate>lambdaQuery().eq(WbsTreePrivate::getPKeyId, pkeyId));
+        // 获取 ContractLog ID列表(原逻辑不变)
+        List<ContractLog> contractLogList = this.contractLogClient.queryContractLogByPrimaryKeyIdAndRecordTimeList(
+                nodePrimaryKeyId, recordTime, contractId, createUser
+        );
+        if (CollUtil.isEmpty(contractLogList)) {
+            return R.data(null);
+        }
+        List<String> logIds = contractLogList.stream()
+                .map(ContractLog::getId)
+                .map(String::valueOf)
+                .collect(Collectors.toList());
+
+        // ========================= 2. 循环外预加载不变资源(核心优化:避免重复IO/查询)=========================
+        // 2.1 查询Wbs节点+Excel模板(仅1次)
+        WbsTreePrivate tableNode = this.wbsTreePrivateService.getOne(
+                Wrappers.<WbsTreePrivate>lambdaQuery().eq(WbsTreePrivate::getPKeyId, pkeyId)
+        );
+        if (tableNode == null) {
+            return R.fail("该数据下无此节点!");
+        }
+        ExcelTab excelTab = excelTabService.getById(tableNode.getExcelId());
+        if (excelTab == null) {
+            return R.fail("未找到Excel模板!");
+        }
 
-                if (tableNode == null) {
-                    return R.fail("该数据下无此节点!");
-                }
+        // 2.2 缓存Excel模板(字节流,仅1次OSS下载)
+        byte[] excelTemplateBytes = downloadOssToBytes(excelTab.getFileUrl());
+        if (excelTemplateBytes == null) {
+            return R.fail("Excel模板下载失败!");
+        }
+
+        // 2.3 缓存HTML模板+DOM解析结果(仅1次下载+解析)
+        String htmlString = this.getHtmlString(pkeyId);
+        if (StrUtil.isEmpty(tableNode.getHtmlUrl()) || StrUtil.isEmpty(htmlString)) {
+            return R.fail("未获取到元素表信息!");
+        }
+        Document htmlDoc = Jsoup.parse(htmlString);
+        Element table = htmlDoc.select("table").first();
+        if (table == null) {
+            return R.fail("HTML中未找到表格!");
+        }
+        Elements trs = table.select("tr");
+        Map<String, int[]> dqIdXYMap = preloadDqIdMap(table); // 预缓存电签坐标
+
+        // 2.4 预查询公共数据(仅1次)
+        ProjectInfo projectInfo = projectInfoService.getById(tableNode.getProjectId());
+        // 日期格式化器(线程不安全,单线程复用)
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
+        sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 修复原拼写错误(GTM→GMT)
+        SimpleDateFormat formatStr = new SimpleDateFormat("yyyy年MM月dd日");
+        String sysIsonline = ParamCache.getValue(CommonConstant.SYS_ISONLINE);
+
+        // ========================= 3. 批量查询业务数据(1次查询替代N次)=========================
+        R<List<Map<String, Object>>> batchDataR = this.getTheLogBusinessData(String.join( ",", logIds), nodePrimaryKeyId, recordTime, contractId);
+        if (CollUtil.isEmpty(batchDataR.getData())) {
+            return R.data(null);
+        }
+        List<Map<String, Object>> businessDataMapList = batchDataR.getData();
+
+        // ========================= 4. 异步生成单个PDF(核心优化:并行处理,提升吞吐量)=========================
+        // 线程池(核心数=CPU核心数*2,避免资源耗尽)
+        ExecutorService pdfGenerateExecutor = new ThreadPoolExecutor(
+                Runtime.getRuntime().availableProcessors() * 2,
+                Runtime.getRuntime().availableProcessors() * 4,
+                60L, TimeUnit.SECONDS,
+                new LinkedBlockingQueue<>(100),
+                new ThreadFactoryBuilder().setNameFormat("pdf-generate-pool-%d").build()
+        );
+
+        List<Future<String>> pdfLocalPathFutures = new ArrayList<>(businessDataMapList.size());
+        for (Map<String, Object> dataMap : businessDataMapList) {
+            if (CollUtil.isEmpty(dataMap)) {
+                continue;
+            }
+            // 提交异步任务:生成单个PDF到本地
+            pdfLocalPathFutures.add(pdfGenerateExecutor.submit(() -> {
+                Workbook wb = null;
+                try {
+                    // 从缓存字节流创建Excel(无OSS重复下载)
+                    wb = new Workbook();
+                    wb.loadFromMHtml(new ByteArrayInputStream(excelTemplateBytes));
+                    Worksheet sheet = wb.getWorksheets().get(0);
+
+                    // 添加项目标题(复用预解析的trs)
+                    addProjectTitle(sheet, trs, projectInfo, sysIsonline);
+
+                    // 填充业务数据(复用DOM缓存和格式化器)
+                    fillBusinessData(sheet, trs, dataMap, sdf, formatStr);
 
-                String htmlString = this.getHtmlString(pkeyId);
+                    // 填充电签信息(复用预缓存的dqIdXYMap)
+                    fillDqInfo(sheet, dqIdXYMap);
 
-                if (StringUtils.isEmpty(tableNode.getHtmlUrl()) || StringUtils.isEmpty(htmlString)) {
-                    return R.fail("未获取到元素表信息!");
+                    // 生成本地PDF(临时文件)
+                    Long fileName = SnowFlakeUtil.getId();
+                    String localPdfPath = file_path + "/pdf//" + fileName + ".pdf";
+                    sheet.saveToPdf(localPdfPath);
+                    return localPdfPath;
+                } finally {
+                    if (wb != null) {
+                        wb.dispose(); // 释放资源
+                    }
                 }
+            }));
+        }
 
-                // 获取清表信息
-                ExcelTab excelTab = excelTabService.getById(tableNode.getExcelId());
-                if (excelTab == null) {
-                    return R.fail("失败");
+        // 等待所有单个PDF生成完成,收集本地路径
+        List<String> localPdfPaths = new ArrayList<>();
+        try {
+            for (Future<String> future : pdfLocalPathFutures) {
+                String localPath = future.get(10, TimeUnit.MINUTES); // 单个PDF生成超时控制
+                if (StrUtil.isNotEmpty(localPath) && FileUtils.exist(localPath)) {
+                    localPdfPaths.add(localPath);
                 }
+            }
+        } catch (Exception e) {
+            return R.fail("PDF生成异常");
+        } finally {
+            pdfGenerateExecutor.shutdown(); // 关闭线程池
+        }
 
-                //获取数据
-                List<Map<String, Object>> businessDataMapList = this.getTheLogBusinessData(dataId, nodePrimaryKeyId, recordTime, contractId).getData();
-                ProjectInfo projectInfo = projectInfoService.getById(tableNode.getProjectId());
-                //处理数据
-                for (Map<String, Object> dataMap : businessDataMapList) {
-                    // 获取excel流 和 html流
-                    Workbook wb = new Workbook();
-                    wb.loadFromMHtml(CommonUtil.getOSSInputStream(excelTab.getFileUrl()));
-                    //获取工作表
-                    Worksheet sheet = wb.getWorksheets().get(0);
-                    Document doc = Jsoup.parse(htmlString);
-                    Element table = doc.select("table").first();
-                    Elements trs = table.select("tr");
-
-                    //添加标题
-                    String sys_isonline = ParamCache.getValue(CommonConstant.SYS_ISONLINE);
-                    if ("20".equals(sys_isonline)) { //甬台温
-                        CellRange[] columns = sheet.getMergedCells();
-                        for (int i = 0; i < columns.length; i++) {
-                            CellRange cellRange = columns[i];
-                            if (cellRange.getStyle().getFont().getSize() >= 12 && (Func.isNotEmpty(cellRange.getValue2()) || Func.isNotEmpty(cellRange.getValue()))) {
-                                String title = projectInfo.getProjectName();
-                                if (title.length() >= 30) {
-                                    cellRange.setRowHeight(40);
-                                    cellRange.getStyle().setWrapText(true);
-                                }
-                                cellRange.getStyle().getFont().setSize(18);
-                                cellRange.getStyle().setHorizontalAlignment(HorizontalAlignType.Center);
-                                cellRange.setText(projectInfo.getProjectName());
+        if (CollUtil.isEmpty(localPdfPaths)) {
+            return R.data(null);
+        }
+
+        // ========================= 5. 合并PDF+上传OSS(本地合并后单次上传,避免多次下载)=========================
+        String mergePdfName = SnowFlakeUtil.getId() + "_" + System.currentTimeMillis() + ".pdf";
+        String mergePdfLocalPath = file_path + "/pdf//" + mergePdfName;
+
+        try {
+            // 合并本地PDF(无需下载OSS文件,直接操作本地文件,效率更高)
+            FileUtils.mergePdfPublicMethods(localPdfPaths, mergePdfLocalPath);
+
+            // 上传合并后的PDF到OSS
+            BladeFile mergeFile = this.newIOSSClient.uploadFile(mergePdfName, mergePdfLocalPath);
+            String mergePdfUrl = mergeFile.getLink();
+
+            // 更新ContractLog的PDF链接(批量更新,避免循环调用)
+            this.contractLogClient.updateTheLogPdfUrlByIds(StringUtils.join(logIds, ","), mergePdfUrl);
+
+            return R.data(mergePdfUrl);
+        } catch (Exception e) {
+            return R.fail("数据异常");
+        } finally {
+            // 清理临时文件(单个PDF+合并后的PDF)
+            localPdfPaths.forEach(FileUtils::del);
+            FileUtils.del(mergePdfLocalPath);
+        }
+    }
+
+    /**
+     * 预缓存电签坐标(仅解析1次DOM)
+     */
+    private Map<String, int[]> preloadDqIdMap(Element table) {
+        Map<String, int[]> dqIdXYMap = new HashMap<>();
+        Elements dqids = table.getElementsByAttribute("dqid");
+        for (Element element : dqids) {
+            String dqid = element.attr("dqid");
+            Elements x1Elements = element.getElementsByAttribute("x1");
+            if (CollUtil.isNotEmpty(x1Elements)) {
+                Element lastX1 = x1Elements.get(x1Elements.size() - 1);
+                int x1 = Func.toInt(lastX1.attr("x1"), 1);
+                int y1 = Func.toInt(lastX1.attr("y1"), 0);
+                dqIdXYMap.put(dqid, new int[]{x1, y1});
+            }
+        }
+        return dqIdXYMap;
+    }
+
+    /**
+     * OSS文件下载为字节数组(仅1次下载,缓存复用)
+     */
+    private byte[] downloadOssToBytes(String ossUrl) {
+        try (InputStream ossIs = CommonUtil.getOSSInputStream(ossUrl);
+             ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            IoUtil.copy(ossIs, baos);
+            return baos.toByteArray();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    /**
+     * 统一添加项目标题(消除重复逻辑)
+     */
+    private void addProjectTitle(Worksheet sheet, Elements trs, ProjectInfo projectInfo, String sysIsonline) {
+        if (projectInfo == null || StrUtil.isEmpty(projectInfo.getProjectName())) {
+            return;
+        }
+        if ("20".equals(sysIsonline)) { // 甬台温
+            CellRange[] columns = sheet.getMergedCells();
+            for (CellRange cellRange : columns) {
+                if (cellRange.getStyle().getFont().getSize() >= 12 && Func.isNotEmpty(cellRange.getValue2())) {
+                    String projectName = projectInfo.getProjectName();
+                    if (projectName.length() >= 30) {
+                        cellRange.setRowHeight(40);
+                        cellRange.getStyle().setWrapText(true);
+                    }
+                    cellRange.getStyle().getFont().setSize(18);
+                    cellRange.getStyle().setHorizontalAlignment(HorizontalAlignType.Center);
+                    cellRange.setText(projectName);
+                    break;
+                }
+            }
+        } else {
+            boolean titleSet = false;
+            for (int i = 1; i < Math.min(6, trs.size()) && !titleSet; i++) {
+                Element tr = trs.get(i);
+                Elements tds = tr.select("td");
+                for (Element td : tds) {
+                    String style = td.attr("style");
+                    if (style.contains("font-size")) {
+                        int fontsize = Integer.parseInt(style.substring(style.indexOf("font-size:") + 10, style.indexOf(".0pt")));
+                        if (StrUtil.isNotEmpty(td.text()) && fontsize >= 12) {
+                            Element prevTd = trs.get(i - 1).select("td").get(0);
+                            if (Func.isEmpty(prevTd.text())) {
+                                int[] xy = parseXyFromElement(prevTd);
+                                sheet.getCellRange(xy[1], xy[0]).setText(projectInfo.getProjectName());
+                                titleSet = true;
                                 break;
                             }
                         }
-                    } else {
-                        for (int i = 1; i < 6; i++) {
-                            Element tr = trs.get(i);
-                            Elements tds = tr.select("td");
-                            for (int j = 0; j < tds.size(); j++) {
-                                Element data = tds.get(j);
-                                String style = data.attr("style");
-                                if (style.contains("font-size")) {
-                                    int fontsize = Integer.parseInt(style.substring(style.indexOf("font-size:") + 10, style.indexOf(".0pt")));
-                                    if (StringUtils.isNotEmpty(data.text()) && fontsize >= 12) {
-                                        Element element = trs.get(i - 1).select("td").get(0);
-                                        String textainfo = element.text();
-                                        if (textainfo == null || textainfo == "" || Func.isEmpty(textainfo)) {
-                                            int x1, y1;
-                                            if ((element.html().indexOf("x1") >= 0 && element.html().indexOf("y1") >= 0) || (element.hasAttr("x1") && element.hasAttr("y1"))) {
-                                                if (element.html().indexOf("el-tooltip") >= 0) {
-                                                    x1 = Integer.parseInt(element.children().get(0).children().get(0).attr("x1"));
-                                                    y1 = Integer.parseInt(element.children().get(0).children().get(0).attr("y1"));
-                                                } else {
-                                                    Elements children = element.children();
-                                                    if (children.size() >= 1) {
-                                                        x1 = Integer.parseInt(element.children().get(0).attr("x1"));
-                                                        y1 = Integer.parseInt(element.children().get(0).attr("y1"));
-                                                    } else {
-                                                        x1 = Integer.parseInt(element.attr("x1"));
-                                                        y1 = Integer.parseInt(element.attr("y1"));
-                                                    }
-                                                }
-                                                if (x1 == 0) {
-                                                    x1 = 1;
-                                                }
-                                                final CellRange cellRange = sheet.getCellRange(y1, x1);
-                                                cellRange.setText(projectInfo.getProjectName());
-                                            }
-                                            break;
-                                        }
-                                    }
-                                }
-                            }
-                        }
                     }
+                }
+            }
+        }
+    }
 
-                    if (ObjectUtil.isNotEmpty(dataMap)) {
-                        for (String val : dataMap.keySet()) {
-                            boolean flag = false;
-                            if (val.indexOf("__") >= 0) {
-                                String[] DataVal = val.split("__");
-                                String[] xy = DataVal[1].split("_");
-                                if (trs.size() > Integer.parseInt(xy[0])) {
-                                    Element trData = trs.get(Integer.parseInt(xy[0]));
-                                    Elements tdDatas = trData.select("td");
-                                    if (tdDatas.size() > Integer.parseInt(xy[1])) {
-                                        Element data = tdDatas.get(Integer.parseInt(xy[1]));
-                                        if (data.html().indexOf("date") >= 0) {
-                                            flag = true;
-                                        }
-                                        if (data.html().indexOf("x1") >= 0 && data.html().indexOf("y1") >= 0) {
-                                            int x1, y1;
+    /**
+     * 解析元素的x1、y1坐标(复用逻辑)
+     */
+    private int[] parseXyFromElement(Element element) {
+        int x1 = 1, y1 = 0;
+        if (element.html().indexOf("x1") >= 0 && element.html().indexOf("y1") >= 0) {
+            if (element.html().indexOf("el-tooltip") >= 0) {
+                x1 = Func.toInt(element.children().get(0).children().get(0).attr("x1"), 1);
+                y1 = Func.toInt(element.children().get(0).children().get(0).attr("y1"), 0);
+            } else {
+                Elements children = element.children();
+                if (CollUtil.isNotEmpty(children)) {
+                    x1 = Func.toInt(children.get(0).attr("x1"), 1);
+                    y1 = Func.toInt(children.get(0).attr("y1"), 0);
+                } else {
+                    x1 = Func.toInt(element.attr("x1"), 1);
+                    y1 = Func.toInt(element.attr("y1"), 0);
+                }
+            }
+        }
+        return new int[]{x1, y1};
+    }
 
-                                            if (data.html().indexOf("el-tooltip") >= 0) {
-                                                x1 = Integer.parseInt(data.children().get(0).children().get(0).attr("x1"));
-                                                y1 = Integer.parseInt(data.children().get(0).children().get(0).attr("y1"));
-                                            } else {
-                                                x1 = Integer.parseInt(data.children().get(0).attr("x1"));
-                                                y1 = Integer.parseInt(data.children().get(0).attr("y1"));
-                                            }
-                                            if (x1 == 0) {
-                                                x1 = 1;
-                                            }
-                                            String myData = dataMap.get(val) + "";
-                                            if (((myData.indexOf("T") >= 0 && myData.indexOf("-") >= 0) || (myData.indexOf(",") >= 0 && myData.indexOf("]") >= 0)) && flag) {
-                                                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-                                                sdf.setTimeZone(TimeZone.getTimeZone("GTM+8"));
-                                                SimpleDateFormat formatStr = new SimpleDateFormat("yyyy年MM月dd日");
-                                                if (myData.indexOf(",") >= 0 && myData.indexOf("]") >= 0) {
-                                                    myData = myData.replace("[", "").replace("]", "").replaceAll("'", "");
-                                                    String[] dataVal = myData.split(",");
-                                                    Date Start_dataStr = sdf.parse(dataVal[0]);
-                                                    Date end_dataStr = sdf.parse(dataVal[1]);
-                                                    String StartDate = formatStr.format(Start_dataStr);
-                                                    String endDate = formatStr.format(end_dataStr);
-                                                    if (StartDate.equals(endDate)) {
-                                                        myData = StartDate;
-                                                    } else {
-                                                        myData = StartDate + "-" + endDate;
-                                                    }
-                                                } else {
-                                                    String[] dataStr = myData.split("T")[0].split("-");
-                                                    if (dataStr.length == 3) {
-                                                        myData = StringUtil.format("{}年{}月{}日", dataStr[0], dataStr[1], Integer.parseInt(dataStr[2]));
-                                                    }
-                                                }
-                                            }
-                                            if (myData.indexOf("lang.String") >= 0) {
-                                                Object obj = dataMap.get(val);
-                                                if (obj instanceof String[]) {
-                                                    String[] dataDate = (String[]) obj;
-                                                    String HtmlEle = data.toString();
-                                                    if (HtmlEle.indexOf("el-date-picker") >= 0) {//时间时间段处理格式
-                                                        if ((dataDate[0].trim()).equals((dataDate[1].trim()))) {
-                                                            myData = dataDate[0];
-                                                        } else {
-                                                            myData = dataDate[0].replace("\"", "") + "-" + dataDate[1].trim().replace("\"", "");
-                                                        }
-                                                    } else {
-                                                        myData = Func.convert(dataDate, String.class).replaceAll(" ", "").replaceAll(",","、");
-                                                        if (myData.startsWith("[") && myData.endsWith("]")) {
-                                                            // 去掉两端的中括号
-                                                            myData=myData.replaceAll("\\[","").replaceAll("]","");
-                                                        }
-                                                    }
+    /**
+     * 填充业务数据(复用缓存和工具类)
+     */
+    private void fillBusinessData(Worksheet sheet, Elements trs, Map<String, Object> dataMap, SimpleDateFormat sdf, SimpleDateFormat formatStr) {
+        for (Map.Entry<String, Object> entry : dataMap.entrySet()) {
+            String val = entry.getKey();
+            if (!val.contains("__")) {
+                continue;
+            }
 
-                                                }
-                                            }
+            // 解析key中的坐标(仅split一次)
+            String[] dataVal = val.split("__");
+            if (dataVal.length < 2) {
+                continue;
+            }
+            String[] xy = dataVal[1].split("_");
+            if (xy.length < 2) {
+                continue;
+            }
+            int trIndex = Func.toInt(xy[0], -1);
+            int tdIndex = Func.toInt(xy[1], -1);
+            if (trIndex < 0 || tdIndex < 0 || trIndex >= trs.size()) {
+                continue;
+            }
 
-                                            if (myData.indexOf("http") >= 0 && (myData.indexOf("aliyuncs") >= 0 || myData.indexOf("183.247.216.148") >= 0)) {
-                                                Element element = trs.get(y1).select("td").get(x1);
-                                                String[] styles = element.attr("style").split(";");
-                                                int Height = 0;
-                                                for (String sty : styles) {
-                                                    if (sty.indexOf("height:") >= 0) {
-                                                        Height = Integer.parseInt(sty.replace("height:", "").replace("px", ""));
-                                                    }
-                                                }
+            Element trData = trs.get(trIndex);
+            Elements tdDatas = trData.select("td");
+            if (tdIndex >= tdDatas.size()) {
+                continue;
+            }
+            Element dataTd = tdDatas.get(tdIndex);
 
-                                                BufferedImage image = ImageIO.read(CommonUtil.getOSSInputStream(myData));
-                                                ExcelPicture pic = sheet.getPictures().add(y1, x1, image);
-                                                pic.setHeight(Height);
-                                                sheet.getCellRange(y1, x1).getStyle().setShrinkToFit(true);
-
-                                            } else if (data.html().indexOf("hc-form-checkbox-group") >= 0) {
-                                                CellRange cellRange = sheet.getCellRange(y1, x1);
-                                                String exceVal = cellRange.getValue().replaceAll(" ", "");
-                                                //如果有□ 代表 自动生成  如果没有 代表后期添加 需要显示html 中的值
-                                                if (exceVal.indexOf("□") >= 0) {
-                                                    if (myData.equals("1")) {
-                                                        cellRange.setValue(exceVal.replace("□", "\u2611"));
-                                                    }
-                                                } else {
-                                                    List<Node> nodes = data.childNodes();
-                                                    Node node = nodes.get(nodes.size() - 1);
-                                                    String dataJson = node.attr(":objs");
-                                                    if (StringUtils.isNotEmpty(dataJson)) {
-                                                        JSONArray jsonArray = JSONArray.parseArray(dataJson);
-                                                        List<Integer> idList = Func.toIntList(myData);
-                                                        int indexx = 0;
-                                                        if (idList.get(0) >= 1) {
-                                                            indexx = idList.get(0) - 1;
-                                                        }
+            // 缓存关键属性
+            boolean isDateComponent = dataTd.html().indexOf("date") >= 0;
+            boolean hasXy = dataTd.html().indexOf("x1") >= 0 && dataTd.html().indexOf("y1") >= 0;
+            if (!hasXy) {
+                continue;
+            }
 
-                                                        String dataInfo = jsonArray.getJSONObject(indexx).getString("name");
-                                                        for (int inx = 1; inx < idList.size(); inx++) {
-                                                            int valIndex = idList.get(inx) - 1;
-                                                            dataInfo = dataInfo + "," + jsonArray.getJSONObject(valIndex).getString("name");
-                                                        }
-                                                        cellRange.setValue(dataInfo);
-                                                    }
+            int[] xyArr = parseXyFromElement(dataTd);
+            int x1 = xyArr[0], y1 = xyArr[1];
+            String myData = Func.toStr(entry.getValue(), "");
 
-                                                }
-                                            } else {
-                                                final CellRange cellRange = sheet.getCellRange(y1, x1);
-                                                cellRange.setText(myData);
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
+            // 日期格式处理(复用格式化器)
+            if (((myData.contains("T") && myData.contains("-")) || (myData.contains(",") && myData.contains("]"))) && isDateComponent) {
+                myData = formatDate(myData, sdf, formatStr);
+            }
+
+            // 字符串数组处理
+            if (myData.contains("lang.String")) {
+                myData = handleStringArray(myData, dataTd);
+            }
+
+            // 图片处理
+            if (myData.startsWith("http") && (myData.contains("aliyuncs") || myData.contains("183.247.216.148"))) {
+                handleImage(sheet, trs, x1, y1, myData);
+                continue;
+            }
+
+            // 复选框组处理
+            if (dataTd.html().indexOf("hc-form-checkbox-group") >= 0) {
+                handleCheckboxGroup(sheet, dataTd, x1, y1, myData);
+                continue;
+            }
+
+            // 普通文本填充
+            sheet.getCellRange(y1, x1).setText(myData);
+        }
+    }
+
+    /**
+     * 日期格式化(复用方法)
+     */
+    private String formatDate(String myData, SimpleDateFormat sdf, SimpleDateFormat formatStr) {
+        try {
+            if (myData.contains(",") && myData.contains("]")) {
+                myData = myData.replace("[", "").replace("]", "").replaceAll("'", "");
+                String[] dateArr = myData.split(",");
+                if (dateArr.length >= 2) {
+                    Date start = sdf.parse(dateArr[0].trim());
+                    Date end = sdf.parse(dateArr[1].trim());
+                    String startStr = formatStr.format(start);
+                    String endStr = formatStr.format(end);
+                    return startStr.equals(endStr) ? startStr : startStr + "-" + endStr;
+                }
+            } else {
+                String[] dateStr = myData.split("T")[0].split("-");
+                if (dateStr.length == 3) {
+                    return StrUtil.format("{}年{}月{}日", dateStr[0], dateStr[1], Integer.parseInt(dateStr[2]));
+                }
+            }
+        } catch (Exception e) {
+
+        }
+        return myData;
+    }
+
+    /**
+     * 字符串数组处理(复用方法)
+     */
+    private String handleStringArray(String myData, Element dataTd) {
+        Object obj = dataTd.data();
+        if (obj instanceof String[]) {
+            String[] dataArr = (String[]) obj;
+            if (dataTd.html().indexOf("el-date-picker") >= 0) {
+                if (dataArr.length >= 2 && dataArr[0].trim().equals(dataArr[1].trim())) {
+                    return dataArr[0].replace("\"", "");
+                } else {
+                    return dataArr[0].replace("\"", "") + "-" + dataArr[1].trim().replace("\"", "");
+                }
+            } else {
+                return Arrays.stream(dataArr)
+                        .collect(Collectors.joining("、"))
+                        .replaceAll("\\[|]", "")
+                        .replaceAll(" ", "");
+            }
+        }
+        return myData;
+    }
+
+    /**
+     * 图片处理(复用方法)
+     */
+    private void handleImage(Worksheet sheet, Elements trs, int x1, int y1, String imageUrl) {
+        try {
+            // 解析单元格高度
+            Element td = trs.get(y1).select("td").get(x1);
+            int height = 0;
+            String style = td.attr("style");
+            if (StrUtil.isNotEmpty(style)) {
+                for (String sty : style.split(";")) {
+                    if (sty.contains("height:")) {
+                        height = Func.toInt(sty.replace("height:", "").replace("px", ""), 0);
+                        break;
                     }
-                    // 组装电签设置
-                    Elements dqids = table.getElementsByAttribute("dqid");
-                    for (Element element : dqids) {
-                        String dqid = element.attr("dqid");
-                        Elements x11 = element.getElementsByAttribute("x1");
-                        if (x11 != null && x11.size() >= 1) {
-                            Element element1 = x11.get(x11.size() - 1);
-                            int x1 = Func.toInt(element1.attr("x1"));
-                            int y1 = Func.toInt(element1.attr("y1"));
-
-                            CellRange cellRange = sheet.getCellRange(y1, x1);
-                            if (cellRange != null) {
-                                // 创建字体
-                                String text = cellRange.getText();
-                                if (StringUtil.hasText(text)) {
-                                    dqid = text + "*" + dqid;
+                }
+            }
+
+            // 下载图片并插入Excel
+            try (InputStream imgIs = CommonUtil.getOSSInputStream(imageUrl)) {
+                BufferedImage image = ImageIO.read(imgIs);
+                ExcelPicture pic = sheet.getPictures().add(y1, x1, image);
+                if (height > 0) {
+                    pic.setHeight(height);
+                }
+                sheet.getCellRange(y1, x1).getStyle().setShrinkToFit(true);
+            }
+        } catch (Exception e) {
+        }
+    }
+
+    /**
+     * 复选框组处理(复用方法)
+     */
+    private void handleCheckboxGroup(Worksheet sheet, Element dataTd, int x1, int y1, String myData) {
+        CellRange cellRange = sheet.getCellRange(y1, x1);
+        String excelVal = Func.toStr(cellRange.getValue(), "").replaceAll(" ", "");
+        if (excelVal.contains("□")) {
+            if ("1".equals(myData)) {
+                cellRange.setValue(excelVal.replace("□", "\u2611"));
+            }
+        } else {
+            List<Node> nodes = dataTd.childNodes();
+            if (CollUtil.isNotEmpty(nodes)) {
+                Node lastNode = nodes.get(nodes.size() - 1);
+                String dataJson = lastNode.attr(":objs");
+                if (StrUtil.isNotEmpty(dataJson)) {
+                    JSONArray jsonArray = JSONArray.parseArray(dataJson);
+                    List<Integer> idList = Func.toIntList(myData);
+                    if (CollUtil.isNotEmpty(idList) && jsonArray.size() > 0) {
+                        StringBuilder sb = new StringBuilder();
+                        for (int id : idList) {
+                            int idx = id - 1;
+                            if (idx >= 0 && idx < jsonArray.size()) {
+                                if (sb.length() > 0) {
+                                    sb.append(",");
                                 }
-                                cellRange.setText(dqid);
-                                cellRange.getCellStyle().getExcelFont().setSize(1);
-                                cellRange.getCellStyle().getExcelFont().setColor(Color.WHITE);
+                                sb.append(jsonArray.getJSONObject(idx).getString("name"));
                             }
                         }
+                        cellRange.setValue(sb.toString());
                     }
-
-                    Long fileName = SnowFlakeUtil.getId();
-                    String onePdfPath = file_path + "/pdf//" + fileName + ".pdf";
-                    sheet.saveToPdf(onePdfPath);
-                    BladeFile bladeFile = this.newIOSSClient.uploadFile(fileName + ".pdf", onePdfPath);
-                    pdfUrls.add(bladeFile.getLink());
-                    wb.dispose();
                 }
             }
-            if (pdfUrls.size() > 0) {
-                try {
-                    String mergePdfPath = file_path + "/pdf//" + SnowFlakeUtil.getId() + ".pdf";
-                    File oldMergePdf = ResourceUtil.getFile(mergePdfPath);
-                    if (oldMergePdf.exists()) {
-                        oldMergePdf.delete();
-                    }
-                    FileUtils.mergePdfPublicMethods(pdfUrls, mergePdfPath);
-                    BladeFile mergeFile = this.newIOSSClient.uploadFile(SnowFlakeUtil.getId() + new Date().getTime() + ".pdf", mergePdfPath);
-
-                    //修改记录,当天的日志所有表的合并pdf都一样,即ids都修改成一样的
-                    this.contractLogClient.updateTheLogPdfUrlByIds(StringUtils.join(ids, ","), mergeFile.getLink());
+        }
+    }
 
-                    return R.data(mergeFile.getLink());
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    return R.fail("数据异常");
+    /**
+     * 填充电签信息(复用预缓存的坐标)
+     */
+    private void fillDqInfo(Worksheet sheet, Map<String, int[]> dqIdXYMap) {
+        for (Map.Entry<String, int[]> entry : dqIdXYMap.entrySet()) {
+            String dqid = entry.getKey();
+            int[] xy = entry.getValue();
+            int x1 = xy[0], y1 = xy[1];
+
+            CellRange cellRange = sheet.getCellRange(y1, x1);
+            if (cellRange != null) {
+                String text = cellRange.getText();
+                if (StringUtils.isNotEmpty( text)) {
+                    dqid = text + "*" + dqid;
                 }
+                cellRange.setText(dqid);
+                cellRange.getCellStyle().getExcelFont().setSize(1);
+                cellRange.getCellStyle().getExcelFont().setColor(Color.WHITE);
             }
         }
-
-        return R.data(null);
     }
 
 //    /**

+ 13 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/utils/FileUtils.java

@@ -1358,4 +1358,17 @@ public class FileUtils {
 
         return originalPath.resolveSibling(newName);
     }
+
+    public static boolean exist(String localPath) {
+        return Files.exists(Paths.get(localPath));
+    }
+
+    public static void del(String s) {
+        File file = new File(s);
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+
+
 }