inspect-server/src/main/java/com/ruoyi/inspect/controller/InsReportController.java
@@ -167,6 +167,14 @@ insReportService.download(id, reportType, reportPageDto.getCreateOrderUser(), response); } @Operation(summary = "æ Word åå§å页æçé¢è§æ¥å PDF") @GetMapping("/preview") @PreAuthorize("@ss.hasPermi('business:reportPreparation')") @PersonalScope(permsName = "business:reportPreparation", objectName = ReportPageDto.class, paramName = "createOrderUser") public void preview(Integer id, Integer reportType, ReportPageDto reportPageDto, HttpServletResponse response) { insReportService.preview(id, reportType, reportPageDto.getCreateOrderUser(), response); } @Operation(summary = "éæ°çææ¥å") @PostMapping("/regenerate") // @PreAuthorize("@ss.hasPermi('business:reportPreparation')") @@ -182,16 +190,13 @@ return Result.success(insReportService.upAll(file)); } /** * éåå°æ£éªä»»å¡ * @param id * @return */ @Operation(summary = "éåå°æ£éªä»»å¡") @Operation(summary = "éåå°æ£éªä»»å¡ææ¥åå®¡æ ¸") @PostMapping("/sendBackTask") public Result sendBackTask(@RequestBody Map<String, Object> param){ Integer id = (Integer) param.get("id"); return Result.success(insReportService.sendBackTask(id)); Integer id = toInteger(param.get("id")); String target = param.get("target") == null ? null : String.valueOf(param.get("target")); String reason = param.get("reason") == null ? null : String.valueOf(param.get("reason")); return Result.success(insReportService.sendBackTask(id, target, reason)); } /** inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsReportReturnRecordMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,7 @@ package com.ruoyi.inspect.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.inspect.pojo.InsReportReturnRecord; public interface InsReportReturnRecordMapper extends BaseMapper<InsReportReturnRecord> { } inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsReport.java
@@ -48,6 +48,12 @@ @Schema(description = "æ£éªæ¥åå°å") private String inspectionUrl; @Schema(description = "æ¹ååçæ£æµæ¥åå®¡æ ¸ç¨¿å°å") private String reviewUrl; @Schema(description = "æ¹ååçæ£éªæ¥åå®¡æ ¸ç¨¿å°å") private String reviewInspectionUrl; /** * æå¨ä¸ä¼ æ¥åå°å */ @@ -65,7 +71,7 @@ private String inspectionTempUrlPdf; /** * 1ï¼æ¹å 0ï¼ä¸æ¹å * 1ï¼æ¹å 0ï¼ä¸æ¹å 2ï¼å·²éå */ @Schema(description = "æ¹åç¶æ") private Integer isRatify; @@ -74,7 +80,7 @@ private String ratifyTell; /** * 1ï¼å®¡æ ¸éè¿ 0ï¼å®¡æ ¸ä¸éè¿ * 1ï¼å®¡æ ¸éè¿ 0ï¼å®¡æ ¸ä¸éè¿ 2ï¼å·²éå */ @Schema(description = "å®¡æ ¸ç¶æ") private Integer isExamine; inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsReportReturnRecord.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,70 @@ package com.ruoyi.inspect.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.io.Serializable; import java.time.LocalDateTime; /** * æ¥åéååç审æ¹ç¶æåæä»¶å¿«ç §ï¼ç¨äºè¿½æº¯å岿¹åçæ¬ã */ @Data @TableName("ins_report_return_record") @Schema(description = "æ¥åéåè®°å½") public class InsReportReturnRecord implements Serializable { @Schema(description = "主é®") @TableId(type = IdType.AUTO) private Long id; @Schema(description = "æ¥åID") private Integer reportId; @Schema(description = "æ£éªåID") private Integer insOrderId; @Schema(description = "éååèç¹ï¼APPROVED å·²æ¹åï¼REPORT_REVIEW æ¥åå®¡æ ¸ï¼REPORT_APPROVAL æ¥åæ¹å") private String fromStage; @Schema(description = "ç®æ èç¹ï¼INSPECTION_TASK æ£éªä»»å¡ï¼REPORT_REVIEW æ¥åå®¡æ ¸") private String targetStage; @Schema(description = "éååå ") private String returnReason; @Schema(description = "éååæäº¤ç¶æï¼0 å¾ æäº¤ï¼1 å·²æäº¤") private Integer state; @Schema(description = "éååå®¡æ ¸ç¶æï¼0 ä¸éè¿ï¼1 éè¿ï¼2 å·²éå") private Integer isExamine; @Schema(description = "éååå®¡æ ¸æ¶é´") private LocalDateTime examineTime; @Schema(description = "éååæ¹åç¶æï¼0 䏿¹åï¼1 æ¹åï¼2 å·²éå") private Integer isRatify; @Schema(description = "éå忹忶é´") private LocalDateTime ratifyTime; @Schema(description = "éååæ£æµæ¥åæä»¶å°å") private String reportUrl; @Schema(description = "éååæ£éªæ¥åæä»¶å°å") private String inspectionUrl; @Schema(description = "éååæ£æµæ¥å临æ¶é¢è§å°å") private String tempUrlPdf; @Schema(description = "éååæ£éªæ¥å临æ¶é¢è§å°å") private String inspectionTempUrlPdf; @Schema(description = "éåæä½äººç¨æ·ID") private Integer operatorId; @Schema(description = "éåè®°å½å建æ¶é´") private LocalDateTime createTime; } inspect-server/src/main/java/com/ruoyi/inspect/service/InsReportService.java
@@ -50,6 +50,9 @@ */ void download(Integer id, Integer reportType, Integer createOrderUser, HttpServletResponse response); /** ä¸ Word ä¸è½½ç¸åçæ°æ®èå´ï¼ä½¿ç¨åºå®å页ç PDF ä¿çæ¥åçå¼ã */ void preview(Integer id, Integer reportType, Integer createOrderUser, HttpServletResponse response); int upAll(MultipartFile file) throws IOException; /** @@ -63,11 +66,10 @@ Long getUnqualifiedCount(InsOrder insOrder); /** * éåå°æ£éªä»»å¡ * @param id * @return * é忥念ç¨ã * targetï¼INSPECTION_TASK é忣éªä»»å¡ï¼REPORT_REVIEW é忥åå®¡æ ¸ã */ boolean sendBackTask(Integer id); boolean sendBackTask(Integer id, String target, String reason); /** * æ¥åå¯¼åº inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
@@ -1856,11 +1856,13 @@ } // æ£éªäººåæäº¤å¤æ ¸æ¶ä¸çææ¥åï¼å ¨é¨è¯éªå®¤å¤æ ¸éè¿åçææ¥åã if (report == null || StringUtils.isBlank(report.getUrl())) { boolean returnedFromReport = report != null && Objects.equals(report.getIsPass(), 0); if (report == null || StringUtils.isBlank(report.getUrl()) || returnedFromReport) { this.generateReport(orderId, DETECTION_REPORT, writeUserId); } // ç´ç»æ¨¡æ¿è®¢ååæ¶çææ£éªæ¥åï¼ä¸¤ä»½æä»¶å ±ç¨å䏿¡å®¡æ¹è®°å½ã if (directTemplateOrder && (report == null || StringUtils.isBlank(report.getInspectionUrl()))) { if (directTemplateOrder && (report == null || StringUtils.isBlank(report.getInspectionUrl()) || returnedFromReport)) { this.generateReport(orderId, INSPECTION_REPORT, writeUserId); } report = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery() inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
@@ -35,6 +35,7 @@ import com.ruoyi.common.constant.InsOrderTypeConstants; import com.ruoyi.common.constant.MenuJumpPathConstants; import com.ruoyi.common.core.domain.Result; import com.ruoyi.common.core.domain.entity.User; import com.ruoyi.common.core.domain.entity.SysMenu; import com.ruoyi.common.core.domain.entity.InformationNotification; import com.ruoyi.common.utils.DateImageUtil; @@ -103,11 +104,18 @@ private static final int DETECTION_REPORT = 0; private static final int INSPECTION_REPORT = 1; private static final String RETURN_TO_INSPECTION_TASK = "INSPECTION_TASK"; private static final String RETURN_TO_REPORT_REVIEW = "REPORT_REVIEW"; private static final int RETURNED_STATUS = 2; @Resource private UserMapper userMapper; @Resource private InsReportMapper insReportMapper; @Resource private InsReportEditSessionMapper insReportEditSessionMapper; @Resource private InsReportReturnRecordMapper insReportReturnRecordMapper; @Resource private SealUsageLedgerService sealUsageLedgerService; @Resource @@ -446,6 +454,9 @@ if (Objects.equals(insReport.getIsRatify(), 1)) { throw new ErrorException("æ¥åå·²æ¹åï¼è¯·å¿é夿ä½"); } if (!Objects.equals(insReport.getState(), 1) || !Objects.equals(insReport.getIsExamine(), 1)) { throw new ErrorException("æ¥åå°æªå®¡æ ¸éè¿ï¼ä¸è½æ¹å"); } if (Objects.equals(insReport.getExamineUserId(), SecurityUtils.getUserId().intValue())) { throw new ErrorException("å®¡æ ¸äººåæ¹å人ä¸è½ä¸ºåä¸äººï¼è¯·éæ°éæ©æ¹å人"); } @@ -499,10 +510,21 @@ } }); //妿æ¹åä¸éè¿ ç´æ¥éåå°æäº¤äººé£è¾¹å» insReport.setState(0); insReport.setIsExamine(0); return insReportMapper.updateById(insReport); // æ¹åä¸éè¿ç´æ¥é忥åå®¡æ ¸ï¼ç±åå®¡æ ¸äººä¿®æ¹åéæ°å®¡æ ¸ãæ¹åã recordReportReturn(insReport, "REPORT_APPROVAL", RETURN_TO_REPORT_REVIEW, StringUtils.isBlank(ratifyTell) ? "æ¹åä¸éè¿" : ratifyTell, insReport.getRatifyTime()); insReport.setState(1); insReport.setExamineTell(ratifyTell); int affected = insReportMapper.updateById(insReport); if (affected != 1) { throw new ErrorException("æ¥åéåå®¡æ ¸å¤±è´¥"); } insReportMapper.update(null, Wrappers.<InsReport>lambdaUpdate() .eq(InsReport::getId, id) .set(InsReport::getIsExamine, RETURNED_STATUS) .set(InsReport::getExamineTime, insReport.getRatifyTime())); expireReportEditSessions(id); return affected; } //è·åæ¹å人çç¾åå°å String signatureUrl; @@ -552,6 +574,7 @@ insReport.setReportSealName(reportSeal == null ? null : reportSeal.getName()); insReport.setSeamSealId(seamSeal == null ? null : seamSeal.getId()); insReport.setSeamSealName(seamSeal == null ? null : seamSeal.getName()); prepareApprovalCopies(insReport); String primaryReportUrl = getPrimaryReportUrl(insReport); String finalUrl = primaryReportUrl.replace("/word", wordUrl); @@ -740,6 +763,37 @@ .collect(Collectors.toList()); } /** æ¹åç« åªåå ¥å¯æ¬ï¼ä¿çæ æ¹åç« çå®¡æ ¸ç¨¿ç¨äºæ¹ååéåä¿®æ¹ã */ private void prepareApprovalCopies(InsReport report) { String primarySource = getPrimaryReportUrl(report); String inspectionSource = report.getInspectionUrl(); report.setReviewUrl(primarySource); report.setReviewInspectionUrl(inspectionSource); report.setUrlS(copyReportForApproval(primarySource)); if (StringUtils.isNotBlank(inspectionSource)) { report.setInspectionUrl(copyReportForApproval(inspectionSource)); } } private String copyReportForApproval(String sourceUrl) { try { Path source = Paths.get(sourceUrl.replace("/word", wordUrl)); if (!Files.exists(source)) { throw new ErrorException("å¾ æ¹åæ¥åæä»¶ä¸åå¨"); } String fileName = "approved-" + UUID.randomUUID() + ".docx"; Path target = Paths.get(wordUrl, fileName); Files.createDirectories(target.getParent()); Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); return "/word/" + fileName; } catch (ErrorException e) { throw e; } catch (Exception e) { log.error("å建æ¥åæ¹å坿¬å¤±è´¥: {}", sourceUrl, e); throw new ErrorException("å建æ¥åæ¹å坿¬å¤±è´¥"); } } private void refreshTemporaryPreviews(InsReport report) { String primaryPdf = wordToPdfTemp(getPrimaryReportUrl(report).replace("/word", wordUrl)); if (StringUtils.isBlank(primaryPdf)) { @@ -865,6 +919,82 @@ log.error("ä¸è½½æ¥å失败ï¼reportId={}", id, e); throw new ErrorException("æ¥åä¸è½½å¤±è´¥"); } } @Override public void preview(Integer id, Integer reportType, Integer createOrderUser, HttpServletResponse response) { int type = reportType == null ? DETECTION_REPORT : reportType; if (id == null || (type != DETECTION_REPORT && type != INSPECTION_REPORT)) { throw new ErrorException("æ¥åææ¥åç±»å䏿£ç¡®"); } InsReport report = insReportMapper.selectDownloadableReport(id, createOrderUser); if (report == null) { throw new ErrorException("æ¥åä¸åå¨ãå°æªçæææ ææ¥ç"); } String sourceUrl = type == INSPECTION_REPORT ? report.getInspectionUrl() : getPrimaryReportUrl(report); if (StringUtils.isBlank(sourceUrl)) { throw new ErrorException("æ¥åæä»¶å°æªçæ"); } try { Path source = resolvePreviewFile(sourceUrl); Path pdf; if (Objects.equals(report.getIsRatify(), 1)) { // å·²æ¹åæ¥åå¿ é¡»ä½¿ç¨æ¹åæ¶çæç PDFï¼ä¿ç PDF ä¸ç¨ç MA ç« åéªç¼ç« ã // inspectionTempUrlPdf å¯è½ä»æåå®¡æ ¸é¶æ®µç临æ¶ç¨¿ï¼ä¸è½ä½ä¸ºæç« æ¥åè¿åã pdf = resolvePreviewFile(sourceUrl.replaceFirst("(?i)\\.docx?$", ".pdf")); if (pdf == null) { throw new ErrorException("æç« æ¥å PDF ä¸åå¨ï¼è¯·éæ°æ¹åçæ"); } } else { pdf = resolvePreviewFile(type == INSPECTION_REPORT ? report.getInspectionTempUrlPdf() : report.getTempUrlPdf()); if (source == null) throw new ErrorException("Word æ¥åæä»¶ä¸åå¨"); if (pdf == null || Files.getLastModifiedTime(pdf).compareTo(Files.getLastModifiedTime(source)) < 0) { // 缺å°é¢è§æ Word æ´æ°åå转æ¢ï¼åä¸çæ¬é夿¥çå¤ç¨ç¼åï¼ä¸æ¹å®¡æ¹æ°æ®ã Path directory = Paths.get(wordUrl).toAbsolutePath().normalize().resolve("preview"); Files.createDirectories(directory); String version = source + ":" + Files.size(source) + ":" + Files.getLastModifiedTime(source); String name = UUID.nameUUIDFromBytes(version.getBytes(java.nio.charset.StandardCharsets.UTF_8)) + ".pdf"; pdf = directory.resolve(name); if (!Files.isRegularFile(pdf)) { Path temporary = Files.createTempFile(directory, "report-", ".pdf"); try { if (wordToPdf(source.toString(), temporary.toString()) == null) { throw new ErrorException("æ¥å转 PDF 失败ï¼è¯·è系管çå"); } Files.move(temporary, pdf, StandardCopyOption.REPLACE_EXISTING); } finally { Files.deleteIfExists(temporary); } } } } String filename = (StringUtils.isBlank(report.getCode()) ? "æ¥å" : report.getCode()) .replaceAll("[\\\\/:*?\"<>|\\p{Cntrl}]", "-") + (type == INSPECTION_REPORT ? "-æ£éªæ¥å.pdf" : "-æ£æµæ¥å.pdf"); response.setContentType("application/pdf"); response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + URLEncoder.encode(filename, "UTF-8").replace("+", "%20")); response.setHeader("Cache-Control", "no-store"); response.setContentLengthLong(Files.size(pdf)); Files.copy(pdf, response.getOutputStream()); } catch (IOException e) { log.error("æ¥åé¢è§å¤±è´¥ï¼reportId={}", id, e); throw new ErrorException("æ¥åé¢è§å¤±è´¥ï¼è¯·è系管çå"); } } /** åªè¯»åæ¥åç®å½å çæä»¶ï¼ç¼ºå¤±æä»¶äº¤ç±è°ç¨æ¹å¤çã */ private Path resolvePreviewFile(String url) throws IOException { if (StringUtils.isBlank(url)) return null; String path = url.replace('\\', '/'); if (!path.startsWith("/word/")) throw new ErrorException("æ¥åæä»¶å°å䏿£ç¡®"); Path root = Paths.get(wordUrl).toAbsolutePath().normalize(); Path file = root.resolve(path.substring("/word/".length())).normalize(); if (!file.startsWith(root)) throw new ErrorException("æ¥åæä»¶å°å䏿£ç¡®"); if (!Files.isRegularFile(file)) return null; if (!file.toRealPath().startsWith(root.toRealPath())) throw new ErrorException("æ¥åæä»¶å°å䏿£ç¡®"); return file; } //æ¹éä¸ä¼ @@ -1234,38 +1364,172 @@ return unqualifiedCount; } /** * éåå°æ£éªä»»å¡ * @param id * @return */ @Override @Transactional(rollbackFor = Exception.class) public boolean sendBackTask(Integer id) { InsReport report = insReportMapper.selectById(id); public boolean sendBackTask(Integer id, String target, String reason) { if (id == null || (!RETURN_TO_INSPECTION_TASK.equals(target) && !RETURN_TO_REPORT_REVIEW.equals(target))) { throw new ErrorException("éå忰䏿£ç¡®"); } if (StringUtils.isBlank(reason)) { throw new ErrorException("请è¾å ¥éååå "); } InsReport report = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery() .eq(InsReport::getId, id) .last("FOR UPDATE")); if (report == null || report.getInsOrderId() == null) { throw new ErrorException("æ¥åä¸å卿æªå ³èæ£éªå"); } // æ¥åç±è®¢åä¸åè¯éªå®¤å ±åå½¢æï¼éåæ¶å ¨é¨æ¢å¤ä¸ºå¾ 夿 ¸ï¼é¿å åªéæºéåä¸ä¸ªè¯éªå®¤ã insOrderStateMapper.update(null, Wrappers.<InsOrderState>lambdaUpdate() .eq(InsOrderState::getInsOrderId, report.getInsOrderId()) .set(InsOrderState::getInsState, 4)); InsSampleUser sampleUser = insSampleUserMapper.selectOne(Wrappers.<InsSampleUser>lambdaQuery() .eq(InsSampleUser::getInsSampleId, report.getInsOrderId()) .orderByDesc(InsSampleUser::getId) .last("limit 1")); if (sampleUser != null) { insSampleUserMapper.deleteById(sampleUser.getId()); if (!Objects.equals(report.getIsPass(), 1)) { throw new ErrorException("æ¥åå·²éåºæ¥åæµç¨ï¼è¯·å¿éå¤éå"); } // ä¿®æ¹æ¥åè¡ä¸ºæªæ¾ç¤º LocalDateTime returnTime = LocalDateTime.now(); recordReportReturn(report, target, reason, returnTime); if (RETURN_TO_REPORT_REVIEW.equals(target)) { if (!Objects.equals(report.getIsRatify(), 1)) { throw new ErrorException("åªæå·²æ¹åæ¥åå¯ä»¥éåå°æ¥åå®¡æ ¸"); } String reviewUrl = report.getReviewUrl(); String reviewInspectionUrl = report.getReviewInspectionUrl(); if (StringUtils.isBlank(reviewUrl)) { reviewUrl = prepareLegacyApprovedReportForReview(report, StringUtils.isNotBlank(report.getUrlS()) ? report.getUrlS() : report.getUrl()); if (StringUtils.isNotBlank(report.getInspectionUrl())) { reviewInspectionUrl = prepareLegacyApprovedReportForReview(report, report.getInspectionUrl()); } } int affected = insReportMapper.update(null, Wrappers.<InsReport>lambdaUpdate() .eq(InsReport::getId, id) .eq(InsReport::getIsRatify, 1) .set(InsReport::getState, 1) .set(InsReport::getIsExamine, RETURNED_STATUS) .set(InsReport::getExamineTime, returnTime) .set(InsReport::getExamineTell, reason) .set(InsReport::getIsRatify, RETURNED_STATUS) .set(InsReport::getRatifyTime, returnTime) .set(InsReport::getRatifyTell, reason) .set(InsReport::getUrlS, Objects.equals(reviewUrl, report.getUrl()) ? null : reviewUrl) .set(StringUtils.isNotBlank(reviewInspectionUrl), InsReport::getInspectionUrl, reviewInspectionUrl) .set(InsReport::getReviewUrl, null) .set(InsReport::getReviewInspectionUrl, null) .set(InsReport::getTempUrlPdf, null) .set(InsReport::getInspectionTempUrlPdf, null)); if (affected != 1) { throw new ErrorException("æ¥åç¶æå·²ååï¼è¯·å·æ°åéè¯"); } expireReportEditSessions(id); return true; } if (!Objects.equals(report.getState(), 1) && !Objects.equals(report.getIsRatify(), 1)) { throw new ErrorException("å½åæ¥åä¸å¨å®¡æ ¸æå·²æ¹åç¶æï¼ä¸è½é忣éªä»»å¡"); } InsOrder order = insOrderMapper.selectById(report.getInsOrderId()); if (order == null) { throw new ErrorException("æ¥åå ³èçæ£éªåä¸åå¨"); } boolean hadApproval = report.getIsRatify() != null; // æ¢å¤è®¢åä¸»ç¶æï¼å¦åæ£éªä»»å¡æ¥è¯¢è¦æ± io.state=1 æ¶ä»æ æ³æ¾å°è¯¥ä»»å¡ã insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate() .eq(InsOrder::getId, report.getInsOrderId()) .set(InsOrder::getState, 1) .set(InsOrder::getInsState, 4)); // æ´ä»½æ¥åç±è®¢åä¸åè¯éªå®¤å ±åå½¢æï¼éåå忣éªå®¤ååå°å¯ä¿®æ¹ãéæ°æäº¤å¤æ ¸çç¶æã insOrderStateMapper.update(null, Wrappers.<InsOrderState>lambdaUpdate() .eq(InsOrderState::getInsOrderId, report.getInsOrderId()) .set(InsOrderState::getInsState, 4) .set(InsOrderState::getVerifyUser, null) .set(InsOrderState::getVerifyTell, reason)); // åªæ¤é夿 ¸äººä»»å¡ï¼ä¿ç忣éªå®¤åæ£éªäººï¼é¿å éåå任塿 人å¯è§ã insSampleUserMapper.delete(Wrappers.<InsSampleUser>lambdaQuery() .eq(InsSampleUser::getInsSampleId, report.getInsOrderId()) .eq(InsSampleUser::getState, 1)); // æ¥åææ¶éåºç¼å¶å表ãåå§æ°æ®å¤æ ¸éè¿åï¼ç°ææµç¨ä¼æ¢å¤æ¥åå¹¶éæ°æäº¤å®¡æ ¸ã insReportMapper.update(null, Wrappers.<InsReport>lambdaUpdate() .eq(InsReport::getId, id) .set(InsReport::getIsPass, 0)); .set(InsReport::getIsPass, 0) .set(InsReport::getState, 0) .set(InsReport::getIsExamine, RETURNED_STATUS) .set(InsReport::getExamineTime, returnTime) .set(InsReport::getExamineTell, reason) .set(InsReport::getIsRatify, hadApproval ? RETURNED_STATUS : null) .set(InsReport::getRatifyTime, hadApproval ? returnTime : null) .set(InsReport::getRatifyTell, reason) .set(InsReport::getUrlS, null) .set(InsReport::getReviewUrl, null) .set(InsReport::getReviewInspectionUrl, null) .set(InsReport::getTempUrlPdf, null) .set(InsReport::getInspectionTempUrlPdf, null)); return false; expireReportEditSessions(id); return true; } private String prepareLegacyApprovedReportForReview(InsReport report, String approvedUrl) { if (StringUtils.isBlank(approvedUrl)) { throw new ErrorException("å岿¹åæ¥åæä»¶ä¸åå¨ï¼æ æ³é忥åå®¡æ ¸"); } try { Path source = Paths.get(approvedUrl.replace("/word", wordUrl)); String fileName = "returned-" + UUID.randomUUID() + ".docx"; Path target = Paths.get(wordUrl, fileName); Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING); ReportSealLayoutUtil.restoreForReview(target.toString()); return "/word/" + fileName; } catch (Exception e) { log.error("å岿¹åæ¥åæ¢å¤å®¡æ ¸ç¨¿å¤±è´¥: {}", approvedUrl, e); throw new ErrorException("å岿¹åæ¥åæ æ³æ¸ 餿§æ¹åä¿¡æ¯ï¼è¯·é忣éªä»»å¡éæ°çæ"); } } private void recordReportReturn(InsReport report, String target, String reason, LocalDateTime returnTime) { recordReportReturn(report, Objects.equals(report.getIsRatify(), 1) ? "APPROVED" : "REPORT_REVIEW", target, reason, returnTime); } private void recordReportReturn(InsReport report, String fromStage, String target, String reason, LocalDateTime returnTime) { InsReportReturnRecord record = new InsReportReturnRecord(); record.setReportId(report.getId()); record.setInsOrderId(report.getInsOrderId()); record.setFromStage(fromStage); record.setTargetStage(target); record.setReturnReason(reason.trim()); record.setState(report.getState()); record.setIsExamine(report.getIsExamine()); record.setExamineTime(report.getExamineTime()); record.setIsRatify(report.getIsRatify()); record.setRatifyTime(report.getRatifyTime()); record.setReportUrl(StringUtils.isNotBlank(report.getUrlS()) ? report.getUrlS() : report.getUrl()); record.setInspectionUrl(report.getInspectionUrl()); record.setTempUrlPdf(report.getTempUrlPdf()); record.setInspectionTempUrlPdf(report.getInspectionTempUrlPdf()); record.setOperatorId(SecurityUtils.getUserId().intValue()); record.setCreateTime(returnTime); if (insReportReturnRecordMapper.insert(record) != 1) { throw new ErrorException("æ¥åéåè®°å½ä¿å失败"); } } private void expireReportEditSessions(Integer reportId) { insReportEditSessionMapper.update(null, Wrappers.<InsReportEditSession>lambdaUpdate() .eq(InsReportEditSession::getReportId, reportId) .in(InsReportEditSession::getStatus, "EDITING", "SAVED", "FAILED", "SAVE_REQUESTED") .set(InsReportEditSession::getStatus, "EXPIRED") .set(InsReportEditSession::getMessage, "æ¥åå·²éå") .set(InsReportEditSession::getUpdateTime, LocalDateTime.now())); } /** inspect-server/src/main/java/com/ruoyi/inspect/service/impl/SealUsageLedgerServiceImpl.java
@@ -1,6 +1,7 @@ package com.ruoyi.inspect.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.exception.ErrorException; @@ -93,6 +94,12 @@ || StringUtils.isBlank(report.getSealNames())) { throw new ErrorException("æ¥åæ¹åä¿¡æ¯ä¸å®æ´ï¼æ æ³çæç¨å°å°è´¦"); } // æ¥åéååéæ°æ¹åä»å±äºåä¸ä»½æ¥åï¼ä¿ç馿¬¡æ¹åçæçç¬ç«ç¨å°è®°å½ï¼ // é¿å å¯ä¸é®å²çªå¯¼è´éæ°æ¹åäºå¡æ´ä½åæ»ã if (ledgerMapper.selectCount(Wrappers.<SealUsageLedger>lambdaQuery() .eq(SealUsageLedger::getReportId, report.getId())) > 0) { return; } User ratifyUser = userMapper.selectById(report.getRatifyUserId()); if (ratifyUser == null || StringUtils.isBlank(ratifyUser.getName())) { throw new ErrorException("æ¾ä¸å°æ¥åæ¹åäººï¼æ æ³çæç¨å°å°è´¦"); inspect-server/src/main/java/com/ruoyi/inspect/util/ReportSealLayoutUtil.java
@@ -1,6 +1,8 @@ package com.ruoyi.inspect.util; import com.aspose.words.Document; import com.aspose.words.Cell; import com.aspose.words.CompositeNode; import com.aspose.words.LayoutCollector; import com.aspose.words.LayoutEnumerator; import com.aspose.words.License; @@ -11,6 +13,7 @@ import com.aspose.words.Paragraph; import com.aspose.words.RelativeHorizontalPosition; import com.aspose.words.RelativeVerticalPosition; import com.aspose.words.Run; import com.aspose.words.SaveFormat; import com.aspose.words.Section; import com.aspose.words.Shape; @@ -75,6 +78,98 @@ document.save(wordPath, SaveFormat.DOCX); } /** ä»å岿¹åç¨¿ä¸æ¸ 餿¹åå å®¹ï¼æ¢å¤éæ°æ¹åæéçå ä½ç¬¦ã */ public static void restoreForReview(String wordPath) throws Exception { applyAsposeLicense(); Document document = new Document(wordPath); List<Shape> shapes = imageShapes(document); Shape signatureShape = null; List<Shape> dateShapes = new ArrayList<>(); for (Shape shape : shapes) { if (hasPlaceholderMarker(shape, "ratifyUrl")) { signatureShape = shape; continue; } if (hasPlaceholderMarker(shape, "ratifyDateUrl")) { dateShapes.add(shape); continue; } // æ¹åæ¶æ·»å çæ¥åç« å MA ç« æé¡µé¢æµ®å¨å®ä½ï¼æä½ç½®æ¸ çè鿝è¾å¾çåèã if (isApprovalSeal(shape)) { shape.remove(); } } if (signatureShape != null) { replaceWithPlaceholder(document, signatureShape, "{{@ratifyUrl}}"); } else if (!restoreApprovalCellPlaceholder(document)) { throw new IllegalStateException("æªæ¾å°æ¹åæ ï¼æ æ³æ¢å¤æ¹åç¾åå ä½ç¬¦"); } // é¨åæ¥åæ¨¡æ¿æ²¡ææ¹åæ¥æå¾çï¼æ¹åæ¶é´ä» å卿°æ®åºä¸ã for (Shape dateShape : dateShapes) { replaceWithPlaceholder(document, dateShape, "{{@ratifyDateUrl}}"); } document.save(wordPath, SaveFormat.DOCX); } private static boolean isApprovalSeal(Shape shape) throws Exception { return shape.getWrapType() != WrapType.INLINE && shape.getRelativeHorizontalPosition() == RelativeHorizontalPosition.PAGE && shape.getRelativeVerticalPosition() == RelativeVerticalPosition.PAGE && shape.getWidth() >= 50D && shape.getWidth() <= 140D && shape.getHeight() >= 50D && shape.getHeight() <= 140D; } private static boolean restoreApprovalCellPlaceholder(Document document) throws Exception { NodeCollection cells = document.getChildNodes(NodeType.CELL, true); for (int i = 0; i < cells.getCount(); i++) { Cell label = (Cell) cells.get(i); if (!"æ¹å".equals(label.getText().replace("\r", "").replace("\u0007", "").trim())) { continue; } Node sibling = label.getNextSibling(); if (!(sibling instanceof Cell)) { continue; } Cell signatureCell = (Cell) sibling; for (Shape shape : imageShapesIn(signatureCell)) { shape.remove(); } Paragraph paragraph = signatureCell.getFirstParagraph(); if (paragraph == null) { throw new IllegalStateException("æ¹åç¾ååå æ ¼ç¼ºå°æ®µè½"); } if (!signatureCell.getText().contains("{{@ratifyUrl}}")) { paragraph.appendChild(new Run(document, "{{@ratifyUrl}}")); } return true; } return false; } private static List<Shape> imageShapesIn(Cell cell) throws Exception { List<Shape> result = new ArrayList<>(); NodeCollection nodes = cell.getChildNodes(NodeType.SHAPE, true); for (int i = 0; i < nodes.getCount(); i++) { Shape shape = (Shape) nodes.get(i); if (shape.hasImage()) { result.add(shape); } } return result; } private static boolean hasPlaceholderMarker(Shape shape, String marker) throws Exception { String alternativeText = shape.getAlternativeText(); return alternativeText != null && alternativeText.contains(marker); } @SuppressWarnings({"rawtypes", "unchecked"}) private static void replaceWithPlaceholder(Document document, Shape shape, String placeholder) { CompositeNode parent = (CompositeNode) shape.getParentNode(); parent.insertBefore(new Run(document, placeholder), shape); shape.remove(); } private static void applyAsposeLicense() throws Exception { try (InputStream inputStream = ReportSealLayoutUtil.class.getResourceAsStream("/lib/license.xml")) { if (inputStream == null) { @@ -93,7 +188,7 @@ return null; } Path path = java.nio.file.Paths.get(imagePath); return Files.isRegularFile(path) ? Files.readAllBytes(path) : null; return Files.readAllBytes(path); } private static void removeMatchingImages(Document document, byte[] imageBytes) throws Exception { @@ -115,12 +210,18 @@ private static void addCoverReportSeal(Document document, String imagePath) throws Exception { Paragraph anchor = firstBodyParagraph(document); LayoutCollector collector = new LayoutCollector(document); Paragraph company = findPageCompanyParagraph(document, collector, 1); Rectangle2D rectangle = company == null ? null : getLayoutRectangle(document, collector, company); PageSetup pageSetup = document.getFirstSection().getPageSetup(); double[] size = fitImage(imagePath, REPORT_MAX_WIDTH, REPORT_MAX_HEIGHT); Shape shape = createFloatingImage(document, imagePath, size[0], size[1]); shape.setLeft(pageSetup.getLeftMargin() + 20D); shape.setTop(pageSetup.getPageHeight() - pageSetup.getBottomMargin() - size[1] - POSITION_GAP); shape.setLeft((pageSetup.getPageWidth() - size[0]) / 2D); // å°é¢ç« ä¸åºé¨æºæåç§°éå ï¼éåç§°çå®é æçä½ç½®ç§»å¨ã double preferredTop = rectangle == null ? pageSetup.getPageHeight() - pageSetup.getBottomMargin() - size[1] - POSITION_GAP : rectangle.getCenterY() - size[1] / 2D; shape.setTop(clamp(preferredTop, 0D, pageSetup.getPageHeight() - size[1])); shape.setBehindText(true); anchor.appendChild(shape); } @@ -157,7 +258,9 @@ private static void addLastPageReportSeal(Document document, String imagePath) throws Exception { int lastPage = document.getPageCount(); LayoutCollector collector = new LayoutCollector(document); Paragraph anchor = findLastPageAnchor(document, collector, lastPage); Paragraph title = findResultTitle(document); // ç»æè¡¨å¯è½è·¨é¡µï¼ç« åºè·éç»ææ é¢ï¼ä¸è½è·éæåä¸é¡µçâæ¥åç»æâã Paragraph anchor = title == null ? findLastPageAnchor(document, collector, lastPage) : title; if (anchor == null) { return; } @@ -167,11 +270,23 @@ Shape shape = createFloatingImage(document, imagePath, size[0], size[1]); shape.setLeft((pageSetup.getPageWidth() - size[0]) / 2D); Rectangle2D rectangle = getLayoutRectangle(document, collector, anchor); double preferredTop = rectangle == null ? pageSetup.getPageHeight() - pageSetup.getBottomMargin() - size[1] - POSITION_GAP : rectangle.getY() + rectangle.getHeight() + POSITION_GAP; double preferredTop; if (title != null) { Paragraph company = findPageCompanyParagraph(document, collector, collector.getStartPageIndex(title)); Rectangle2D companyRectangle = company == null ? null : getLayoutRectangle(document, collector, company); // ç« çä¸å¿å¯¹åæºæåç§°ä¸ç»ææ é¢ç»æçåºåï¼å è®¸ä¼¸å ¥ä¸é¡µè¾¹è·ã double headingTop = companyRectangle == null || rectangle == null ? (rectangle == null ? pageSetup.getTopMargin() : rectangle.getY()) : Math.min(companyRectangle.getY(), rectangle.getY()); double headingBottom = rectangle == null ? headingTop : rectangle.getMaxY(); preferredTop = (headingTop + headingBottom - size[1]) / 2D; } else { preferredTop = rectangle == null ? pageSetup.getPageHeight() - pageSetup.getBottomMargin() - size[1] - POSITION_GAP : rectangle.getY() + rectangle.getHeight() + POSITION_GAP; } shape.setTop(clamp(preferredTop, pageSetup.getTopMargin(), title == null ? pageSetup.getTopMargin() : 0D, pageSetup.getPageHeight() - pageSetup.getBottomMargin() - size[1])); shape.setBehindText(true); anchor.appendChild(shape); @@ -180,7 +295,14 @@ private static Shape createFloatingImage(Document document, String imagePath, double width, double height) throws Exception { Shape shape = new Shape(document, ShapeType.IMAGE); shape.getImageData().setImage(imagePath); // Aspose 15 伿 Linux ç / å¼å¤´è·¯å¾å½ä½ç¸å¯¹è·¯å¾ï¼å¯è½çæç©ºå¾çä¸ä¸æ¥éã // éè¿æµåµå ¥çå®å¾çå 容ï¼é¿å è·¯å¾è¢«æææ¡£çèµæºå°å忬¡è§£æã try (InputStream imageStream = Files.newInputStream(java.nio.file.Paths.get(imagePath))) { shape.getImageData().setImage(imageStream); } if (!shape.hasImage()) { throw new IllegalStateException("æ¥åå°ç« å¾çåµå ¥å¤±è´¥"); } shape.setWidth(width); shape.setHeight(height); shape.setWrapType(WrapType.NONE); @@ -220,6 +342,42 @@ return fallback; } private static Paragraph findPageCompanyParagraph(Document document, LayoutCollector collector, int page) throws Exception { Paragraph company = null; NodeCollection paragraphs = document.getChildNodes(NodeType.PARAGRAPH, true); for (int i = 0; i < paragraphs.getCount(); i++) { Paragraph paragraph = (Paragraph) paragraphs.get(i); if (paragraph.getAncestor(NodeType.BODY) != null && paragraph.getAncestor(NodeType.CELL) == null && collector.getStartPageIndex(paragraph) == page && normalizedText(paragraph).endsWith("å ¬å¸")) { company = paragraph; } } return company; } private static Paragraph findResultTitle(Document document) throws Exception { NodeCollection paragraphs = document.getChildNodes(NodeType.PARAGRAPH, true); for (int i = 0; i < paragraphs.getCount(); i++) { Paragraph paragraph = (Paragraph) paragraphs.get(i); if (paragraph.getAncestor(NodeType.BODY) == null || paragraph.getAncestor(NodeType.CELL) != null) { continue; } String text = normalizedText(paragraph); if ("æ£éªç»æ".equals(text) || "æ£æµç»æ".equals(text)) { return paragraph; } } return null; } private static String normalizedText(Paragraph paragraph) { return paragraph.getText().replaceAll("[\\s\\u3000]+", ""); } private static Paragraph findBodyParagraphContaining(Document document, String text, LayoutCollector collector, Integer page) throws Exception { inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanVO.java
@@ -63,6 +63,9 @@ @Schema(description = "çç±") private String verifyTell; @Schema(description = "æ¥åéåå°æ£éªä»»å¡çæ¥æºï¼APPROVED 为æ¹åç¯èï¼REPORT_REVIEW ä¸ºå®¡æ ¸ç¯è") private String reportReturnSource; private String sonLaboratory; inspect-server/src/main/resources/mapper/InsOrderRatesMapper.xml
@@ -17,6 +17,7 @@ left join ins_order_standard_template iost on iost.ins_order_id = io.id left join standard_template st on st.id = iost.template_id where iost.id is not null and io.state != 5 group by io.id) a <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''"> ${ew.customSqlSegment} @@ -27,8 +28,10 @@ <select id="selectTemplateRatesDetail" resultType="com.ruoyi.inspect.dto.TemplateRateDto"> select iost.number, iost.name, COALESCE(st.price, 0) as price from ins_order_standard_template iost join ins_order io on io.id = iost.ins_order_id left join standard_template st on st.id = iost.template_id where iost.ins_order_id = #{insOrderId} and io.state != 5 order by iost.sort, iost.id </select> inspect-server/src/main/resources/mapper/InsReportMapper.xml
@@ -49,6 +49,10 @@ <if test='queryStatus == 2'> and ir.state = 1 and ir.is_examine = 1 and (ir.is_ratify != 1 or ir.is_ratify is null) </if> <!-- å·²éå --> <if test='queryStatus == 3'> and (ir.is_examine = 2 or ir.is_ratify = 2) </if> </if> group by ir.id ) a @@ -102,11 +106,11 @@ case when state = 0 then 'å¾ æäº¤' when state = 1 then 'å·²æäº¤' else '' end state, examine_user, DATE_FORMAT(examine_time, '%Y-%m-%d %H:%i:%s') examine_time, case when is_examine = 0 then 'ä¸éè¿' when is_examine = 1 then 'éè¿' else '' end is_examine, case when is_examine = 0 then 'ä¸éè¿' when is_examine = 1 then 'éè¿' when is_examine = 2 then 'å·²éå' else '' end is_examine, examine_tell, ratify_user, DATE_FORMAT(ratify_time, '%Y-%m-%d %H:%i:%s') ratify_time, case when is_ratify = 0 then '䏿¹å' when is_ratify = 1 then 'æ¹å' else '' end is_ratify, case when is_ratify = 0 then '䏿¹å' when is_ratify = 1 then 'æ¹å' when is_ratify = 2 then 'å·²éå' else '' end is_ratify, ratify_tell from ( select @@ -147,6 +151,10 @@ <if test='queryStatus == 2'> and ir.state = 1 and ir.is_examine = 1 and (ir.is_ratify != 1 or ir.is_ratify is null) </if> <!-- å·²éå --> <if test='queryStatus == 3'> and (ir.is_examine = 2 or ir.is_ratify = 2) </if> </if> group by ir.id ) a inspect-server/src/main/resources/mapper/InsSampleMapper.xml
@@ -195,6 +195,13 @@ ELSE ios.ins_state END AS ins_state, ios.verify_tell, CASE WHEN ios.ins_state = 4 AND a.report_is_pass = 0 THEN ( SELECT rr.from_stage FROM ins_report_return_record rr WHERE rr.ins_order_id = a.id AND rr.target_stage = 'INSPECTION_TASK' ORDER BY rr.id DESC LIMIT 1 ) ELSE NULL END AS report_return_source, verify_user FROM ( @@ -240,6 +247,7 @@ io.type_source, io.ifs_inventory_id, ira.id ins_report_id, ira.is_pass report_is_pass, ira.url, ira.url_s, ira.temp_url_pdf,