| | |
| | | package com.ruoyi.inspect.service.impl; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.hutool.json.JSONUtil; |
| | | import com.alibaba.excel.EasyExcel; |
| | | import com.alibaba.excel.ExcelWriter; |
| | |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.deepoove.poi.XWPFTemplate; |
| | | import com.deepoove.poi.config.Configure; |
| | | import com.deepoove.poi.data.PictureRenderData; |
| | | import com.deepoove.poi.data.PictureType; |
| | | import com.deepoove.poi.data.Pictures; |
| | | import com.itextpdf.text.BadElementException; |
| | | import com.itextpdf.text.DocumentException; |
| | |
| | | import com.itextpdf.text.pdf.PdfReader; |
| | | import com.itextpdf.text.pdf.PdfStamper; |
| | | import com.ruoyi.basic.mapper.IfsInventoryQuantityMapper; |
| | | import com.ruoyi.basic.mapper.SealConfigMapper; |
| | | import com.ruoyi.basic.pojo.IfsInventoryQuantity; |
| | | import com.ruoyi.basic.pojo.SealConfig; |
| | | import com.ruoyi.basic.vo.IfsInventoryQuantityVO; |
| | | import com.ruoyi.common.config.WechatProperty; |
| | | 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; |
| | | import com.ruoyi.common.utils.QueryWrappers; |
| | |
| | | import com.ruoyi.inspect.service.InsOrderService; |
| | | import com.ruoyi.inspect.service.InsReportService; |
| | | import com.ruoyi.inspect.service.RawMaterialOrderService; |
| | | import com.ruoyi.inspect.service.SealUsageLedgerService; |
| | | import com.ruoyi.inspect.util.PreserveReportPicturePlaceholderHandler; |
| | | import com.ruoyi.inspect.util.ReportSealLayoutUtil; |
| | | import com.ruoyi.inspect.util.ReportSignaturePictureUtils; |
| | | import com.ruoyi.system.mapper.UserMapper; |
| | | import com.ruoyi.system.service.ISysMenuService; |
| | | import com.ruoyi.system.service.InformationNotificationService; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.xwpf.usermodel.*; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import jakarta.annotation.Resource; |
| | | import javax.imageio.ImageIO; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.awt.image.BufferedImage; |
| | | import java.io.*; |
| | | import java.net.URLEncoder; |
| | |
| | | |
| | | 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 |
| | | private InsOrderStateMapper insOrderStateMapper; |
| | | @Resource |
| | | private InsProductMapper insProductMapper; |
| | | @Resource |
| | | private InformationNotificationService informationNotificationService; |
| | | @Resource |
| | | private ISysMenuService menuService; |
| | | @Value("${wordUrl}") |
| | | private String wordUrl; |
| | | @Value("${file.path}") |
| | |
| | | private InsOrderMapper insOrderMapper; |
| | | @Resource |
| | | private IfsInventoryQuantityMapper ifsInventoryQuantityMapper; |
| | | @Resource |
| | | private SealConfigMapper sealConfigMapper; |
| | | @Resource |
| | | private InsUnqualifiedHandlerMapper insUnqualifiedHandlerMapper; |
| | | @Resource |
| | |
| | | if (userId == null) { |
| | | throw new ErrorException("缺少审核人"); |
| | | } |
| | | ensureReportPreparationPermission(userId, "审核人"); |
| | | insReport.setExamineUserId(userId);//审核人 |
| | | insReport.setWriteTime(LocalDateTime.now());//提交时间 |
| | | //获取提交人的签名地址 |
| | |
| | | info.setViewStatus(false); |
| | | info.setJumpPath(MenuJumpPathConstants.REPORT_PREPARATION); |
| | | informationNotificationService.addInformationNotification(info); |
| | | String primaryReportUrl = getPrimaryReportUrl(insReport); |
| | | |
| | | // 判断是否是原材料 需要替换****成供应商 |
| | | IfsInventoryQuantity one = ifsInventoryQuantityMapper.selectOne(new LambdaQueryWrapper<IfsInventoryQuantity>() |
| | | .eq(IfsInventoryQuantity::getId, order.getIfsInventoryId())); |
| | |
| | | }}; |
| | | writeMarksToReportFiles(insReport, writeMarks); |
| | | |
| | | // 修改临时pdf |
| | | String tempUrlPdf = wordToPdfTemp(primaryReportUrl.replace("/word", wordUrl)); |
| | | insReport.setTempUrlPdf("/word/" + tempUrlPdf); |
| | | refreshTemporaryPreviews(insReport); |
| | | |
| | | insReportMapper.updateById(insReport); |
| | | |
| | |
| | | .set(InsReport::getIsExamine, null)); |
| | | |
| | | return 1; |
| | | } |
| | | |
| | | /** |
| | | * 指定审核人/批准人时,确认对方登录后能看到「报告编制」页面, |
| | | * 避免指定后登录只剩首页、无法继续审批。 |
| | | */ |
| | | private void ensureReportPreparationPermission(Integer userId, String userLabel) { |
| | | List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId.longValue()); |
| | | if (!containsReportPreparation(menus)) { |
| | | throw new ErrorException("所选" + userLabel + "没有「业务管理 → 报告编制」页面权限,请先在「系统管理 → 用户管理」为其分配对应角色"); |
| | | } |
| | | } |
| | | |
| | | private boolean containsReportPreparation(List<SysMenu> menus) { |
| | | if (CollectionUtils.isEmpty(menus)) { |
| | | return false; |
| | | } |
| | | for (SysMenu menu : menus) { |
| | | if (Long.valueOf(1129L).equals(menu.getMenuId()) |
| | | || "reportPreparation".equals(menu.getPath())) { |
| | | return true; |
| | | } |
| | | if (containsReportPreparation(menu.getChildren())) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | //审核 |
| | |
| | | if (userId == null) { |
| | | throw new ErrorException("缺少批准人"); |
| | | } |
| | | ensureReportPreparationPermission(userId, "批准人"); |
| | | if (Objects.equals(insReport.getExamineUserId(), userId)) { |
| | | throw new ErrorException("审核人和批准人不能为同一人,请重新选择批准人"); |
| | | } |
| | |
| | | info.setViewStatus(false); |
| | | info.setJumpPath(MenuJumpPathConstants.REPORT_PREPARATION); |
| | | informationNotificationService.addInformationNotification(info); |
| | | String primaryReportUrl = getPrimaryReportUrl(insReport); |
| | | Map<String, Object> examineMarks = new HashMap<String, Object>() {{ |
| | | put("examineUrl", ReportSignaturePictureUtils.create(imgUrl, signatureUrl)); |
| | | put("examineDateUrl", Pictures.ofStream(DateImageUtil.createDateImage(null)).create()); |
| | | }}; |
| | | writeMarksToReportFiles(insReport, examineMarks); |
| | | |
| | | // 修改临时pdf |
| | | String tempUrlPdf = wordToPdfTemp(primaryReportUrl.replace("/word", wordUrl)); |
| | | insReport.setTempUrlPdf("/word/" + tempUrlPdf); |
| | | refreshTemporaryPreviews(insReport); |
| | | |
| | | // 发送企业微信通知(通知批准人审批) |
| | | threadPoolTaskExecutor.execute(() -> { |
| | |
| | | message += "\n批次号: " + ifsInventoryQuantity.getUpdateBatchNo(); |
| | | } |
| | | //发送企业微信消息通知 提交复核 |
| | | try { |
| | | WxCpUtils.inform(sendUserAccount, message, null); |
| | | } catch (Exception e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | // try { |
| | | // WxCpUtils.inform(sendUserAccount, message, null); |
| | | // } catch (Exception e) { |
| | | // throw new RuntimeException(e); |
| | | // } |
| | | }); |
| | | insReportMapper.updateById(insReport); |
| | | |
| | |
| | | //批准 |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int ratifyReport(Integer id, Integer isRatify, String ratifyTell) { |
| | | InsReport insReport = insReportMapper.selectById(id); |
| | | public int ratifyReport(Integer id, Integer isRatify, String ratifyTell, String sealIds, |
| | | Integer maSealId, Integer reportSealId, Integer seamSealId) { |
| | | if (id == null || (isRatify == null || (isRatify != 0 && isRatify != 1))) { |
| | | throw new ErrorException("批准参数不正确"); |
| | | } |
| | | InsReport insReport = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery() |
| | | .eq(InsReport::getId, id) |
| | | .last("FOR UPDATE")); |
| | | if (insReport == null) { |
| | | throw new ErrorException("报告不存在"); |
| | | } |
| | | 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("审核人和批准人不能为同一人,请重新选择批准人"); |
| | | } |
| | |
| | | } |
| | | }); |
| | | |
| | | //如果批准不通过 直接退回到提交人那边去 |
| | | 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; |
| | |
| | | InsOrder order = insOrderMapper.selectById(insOrderId); |
| | | boolean isRawMater = order.getTypeSource() != null && order.getTypeSource().equals(1); |
| | | |
| | | //获取场所的报告专用章 |
| | | String sealUrl; |
| | | String laboratory = insOrderMapper.selectById(insReport.getInsOrderId()).getLaboratory(); |
| | | try { |
| | | String type = ""; |
| | | if (isRawMater && order.getOrderType().equals(InsOrderTypeConstants.ENTER_THE_FACTORY)) { |
| | | type = "进厂报告"; |
| | | } else { |
| | | type = "委托报告"; |
| | | } |
| | | sealUrl = insReportMapper.getLaboratoryByName(laboratory, type); |
| | | } catch (Exception e) { |
| | | throw new ErrorException(laboratory + "找不到报告专用章"); |
| | | // 新版按用途选择:MA章、报告章(模板 seal1/seal2)、PDF骑缝章。 |
| | | // 兼容旧前端:只传 sealIds 时,第一枚章继续作为报告章和骑缝章,MA章留空。 |
| | | SealConfig maSeal; |
| | | SealConfig reportSeal; |
| | | SealConfig seamSeal; |
| | | boolean positionMode = maSealId != null || reportSealId != null || seamSealId != null; |
| | | if (positionMode) { |
| | | maSeal = resolveSeal(maSealId); |
| | | reportSeal = resolveSeal(reportSealId); |
| | | seamSeal = resolveSeal(seamSealId); |
| | | } else { |
| | | SealConfig legacySeal = resolveSeals(sealIds).get(0); |
| | | maSeal = null; |
| | | reportSeal = legacySeal; |
| | | seamSeal = legacySeal; |
| | | } |
| | | if (StringUtils.isBlank(sealUrl)) { |
| | | throw new ErrorException(laboratory + "找不到报告专用章"); |
| | | |
| | | LinkedHashMap<Integer, SealConfig> chosenById = new LinkedHashMap<>(); |
| | | Arrays.asList(maSeal, reportSeal, seamSeal).stream() |
| | | .filter(Objects::nonNull) |
| | | .forEach(seal -> chosenById.put(seal.getId(), seal)); |
| | | List<SealConfig> chosenSeals = new ArrayList<>(chosenById.values()); |
| | | if (chosenSeals.isEmpty()) { |
| | | throw new ErrorException("请至少选择一种报告印章"); |
| | | } |
| | | |
| | | insReport.setSealIds(chosenSeals.stream().map(s -> String.valueOf(s.getId())).collect(Collectors.joining(","))); |
| | | insReport.setSealNames(chosenSeals.stream().map(SealConfig::getName).collect(Collectors.joining(","))); |
| | | insReport.setMaSealId(maSeal == null ? null : maSeal.getId()); |
| | | insReport.setMaSealName(maSeal == null ? null : maSeal.getName()); |
| | | insReport.setReportSealId(reportSeal == null ? null : reportSeal.getId()); |
| | | 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); |
| | | |
| | | Map<String, Object> ratifyMarks = new HashMap<String, Object>() {{ |
| | | put("ratifyUrl", ReportSignaturePictureUtils.create(imgUrl, signatureUrl)); |
| | | put("ratifyDateUrl", Pictures.ofStream(DateImageUtil.createDateImage(null)).create()); |
| | | put("seal1", Pictures.ofLocal(imgUrl + "/" + sealUrl).create()); |
| | | put("seal2", Pictures.ofLocal(imgUrl + "/" + sealUrl).create()); |
| | | }}; |
| | | writeMarksToReportFiles(insReport, ratifyMarks); |
| | | Map<String, Object> ratifyMarks = new HashMap<>(); |
| | | ratifyMarks.put("ratifyUrl", ReportSignaturePictureUtils.create(imgUrl, signatureUrl)); |
| | | ratifyMarks.put("ratifyDateUrl", Pictures.ofStream(DateImageUtil.createDateImage(null)).create()); |
| | | if (reportSeal != null) { |
| | | ratifyMarks.put("seal1", createSealPicture(reportSeal.getAddress())); |
| | | ratifyMarks.put("seal2", createSealPicture(reportSeal.getAddress())); |
| | | } |
| | | writeApprovalMarksToReportFiles(insReport, ratifyMarks); |
| | | |
| | | // 印章必须使用浮动定位,否则行内图片会撑高段落/表格并改变原报告分页。 |
| | | for (String reportUrl : getApprovalReportUrls(insReport)) { |
| | | try { |
| | | ReportSealLayoutUtil.apply( |
| | | reportUrl.replace("/word", wordUrl), |
| | | reportSeal == null ? null : Paths.get(imgUrl, reportSeal.getAddress()).toString(), |
| | | maSeal == null ? null : Paths.get(imgUrl, maSeal.getAddress()).toString()); |
| | | } catch (Exception e) { |
| | | log.error("报告印章浮动定位失败: {}", reportUrl, e); |
| | | throw new ErrorException("报告盖章失败,请检查印章图片和报告文件"); |
| | | } |
| | | } |
| | | |
| | | // 修改临时pdf |
| | | insReport.setTempUrlPdf(primaryReportUrl.replace(".docx", ".pdf")); |
| | |
| | | insOrderMapper.updateById(insOrder); |
| | | |
| | | for (String reportUrl : getApprovalReportUrls(insReport)) { |
| | | wordToPdf(reportUrl.replace("/word", wordUrl), sealUrl, |
| | | wordToPdf(reportUrl.replace("/word", wordUrl), |
| | | null, |
| | | seamSeal == null ? null : seamSeal.getAddress(), |
| | | isRawMater && order.getOrderType().equals(InsOrderTypeConstants.ENTER_THE_FACTORY)); |
| | | } |
| | | |
| | |
| | | }); |
| | | } |
| | | |
| | | return insReportMapper.updateById(insReport); |
| | | int affected = insReportMapper.updateById(insReport); |
| | | if (affected != 1) { |
| | | throw new ErrorException("报告批准状态保存失败"); |
| | | } |
| | | sealUsageLedgerService.recordApprovedReport(insReport); |
| | | return affected; |
| | | } |
| | | |
| | | /** |
| | | * 解析批准时选择的印章(逗号分隔的印章id)。 |
| | | * 未传或全部无效时回退到第一个启用印章(默认“1号章”)。 |
| | | */ |
| | | private List<SealConfig> resolveSeals(String sealIds) { |
| | | List<SealConfig> chosen = new ArrayList<>(); |
| | | if (StringUtils.isNotBlank(sealIds)) { |
| | | List<Integer> ids = new ArrayList<>(); |
| | | for (String s : sealIds.split(",")) { |
| | | if (StringUtils.isNotBlank(s)) { |
| | | ids.add(Integer.valueOf(s.trim())); |
| | | } |
| | | } |
| | | if (!ids.isEmpty()) { |
| | | Map<Integer, SealConfig> byId = sealConfigMapper.selectBatchIds(ids).stream() |
| | | .collect(Collectors.toMap(SealConfig::getId, x -> x)); |
| | | for (Integer sid : ids) { |
| | | SealConfig seal = byId.get(sid); |
| | | if (seal != null && Objects.equals(seal.getStatus(), 1) |
| | | && StringUtils.isNotBlank(seal.getAddress())) { |
| | | chosen.add(seal); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | if (chosen.isEmpty()) { |
| | | List<SealConfig> enabled = sealConfigMapper.selectList(Wrappers.<SealConfig>lambdaQuery() |
| | | .eq(SealConfig::getStatus, 1).orderByAsc(SealConfig::getCode)); |
| | | if (!enabled.isEmpty()) { |
| | | chosen.add(enabled.get(0)); |
| | | } |
| | | } |
| | | if (chosen.isEmpty()) { |
| | | throw new ErrorException("未找到可用印章,请先在印章管理中维护并启用印章"); |
| | | } |
| | | return chosen; |
| | | } |
| | | |
| | | private SealConfig resolveSeal(Integer sealId) { |
| | | if (sealId == null) { |
| | | return null; |
| | | } |
| | | SealConfig seal = sealConfigMapper.selectById(sealId); |
| | | if (seal == null) { |
| | | throw new ErrorException("所选印章不存在,请重新选择"); |
| | | } |
| | | if (!Objects.equals(seal.getStatus(), 1)) { |
| | | throw new ErrorException("印章“" + seal.getName() + "”已停用,请重新选择"); |
| | | } |
| | | if (StringUtils.isBlank(seal.getAddress())) { |
| | | throw new ErrorException("印章“" + seal.getName() + "”未上传图片,请重新选择"); |
| | | } |
| | | return seal; |
| | | } |
| | | |
| | | /** |
| | | * 按图片内容识别印章格式,兼容历史数据中扩展名被保存为“_png”等非标准文件名的情况。 |
| | | */ |
| | | private PictureRenderData createSealPicture(String sealUrl) { |
| | | Path sealPath = Paths.get(imgUrl, sealUrl); |
| | | if (!Files.isRegularFile(sealPath)) { |
| | | throw new ErrorException("印章图片文件不存在,请重新上传印章图片"); |
| | | } |
| | | try { |
| | | byte[] imageBytes = Files.readAllBytes(sealPath); |
| | | PictureType pictureType = PictureType.suggestFileType(imageBytes); |
| | | return Pictures.ofBytes(imageBytes, pictureType).create(); |
| | | } catch (Exception e) { |
| | | log.error("读取印章图片失败: {}", sealPath, e); |
| | | throw new ErrorException("印章图片无法识别,请重新上传 jpg 或 png 图片"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public int wordInsertUrl(Map<String, Object> map, String url) { |
| | | Configure configure = Configure.builder() |
| | | .setValidErrorHandler(new PreserveReportPicturePlaceholderHandler()) |
| | | .build(); |
| | | XWPFTemplate template = XWPFTemplate.compile(url, configure).render(map); |
| | | try { |
| | | template.writeAndClose(Files.newOutputStream(Paths.get(url))); |
| | | } catch (IOException e) { |
| | | throw new RuntimeException(e); |
| | | } |
| | | return 1; |
| | | } |
| | | |
| | | private int wordInsertApprovalMarks(Map<String, Object> map, String url) { |
| | | Configure configure = Configure.builder() |
| | | .setValidErrorHandler(new PreserveReportPicturePlaceholderHandler( |
| | | new HashSet<>(Arrays.asList("seal1", "seal2")))) |
| | | .build(); |
| | | XWPFTemplate template = XWPFTemplate.compile(url, configure).render(map); |
| | | try { |
| | |
| | | .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)) { |
| | | throw new ErrorException("检测报告预览生成失败"); |
| | | } |
| | | report.setTempUrlPdf("/word/" + primaryPdf); |
| | | if (StringUtils.isNotBlank(report.getInspectionUrl())) { |
| | | String inspectionPdf = wordToPdfTemp(report.getInspectionUrl().replace("/word", wordUrl)); |
| | | if (StringUtils.isBlank(inspectionPdf)) { |
| | | throw new ErrorException("检验报告预览生成失败"); |
| | | } |
| | | report.setInspectionTempUrlPdf("/word/" + inspectionPdf); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public String prepareEditedReportPreview(String absoluteDocxPath) { |
| | | String pdfName = wordToPdfTemp(absoluteDocxPath); |
| | | if (StringUtils.isBlank(pdfName)) { |
| | | throw new ErrorException("报告预览生成失败"); |
| | | } |
| | | return "/word/" + pdfName; |
| | | } |
| | | |
| | | private void writeMarksToReportFiles(InsReport report, Map<String, Object> marks) { |
| | | for (String reportUrl : getApprovalReportUrls(report)) { |
| | | wordInsertUrl(marks, reportUrl.replace("/word", wordUrl)); |
| | | } |
| | | } |
| | | |
| | | private void writeApprovalMarksToReportFiles(InsReport report, Map<String, Object> marks) { |
| | | for (String reportUrl : getApprovalReportUrls(report)) { |
| | | wordInsertApprovalMarks(marks, reportUrl.replace("/word", wordUrl)); |
| | | } |
| | | } |
| | | |
| | |
| | | throw new ErrorException("报告文件不存在,请联系管理员"); |
| | | } |
| | | try { |
| | | String filename = file.getFileName().toString(); |
| | | // 物理文件可能是 reviewed/edited UUID;对用户始终使用业务报告编号。 |
| | | String reportNumber = StringUtils.isBlank(report.getCode()) ? "报告" : report.getCode(); |
| | | reportNumber = reportNumber.replaceAll("[\\\\/:*?\"<>|\\p{Cntrl}]", "-").trim(); |
| | | String suffix = StringUtils.isNotBlank(report.getInspectionUrl()) |
| | | ? (Objects.equals(reportType, INSPECTION_REPORT) ? "-检验报告" : "-检测报告") : ""; |
| | | String filename = reportNumber + suffix + ".docx"; |
| | | String encodedName = URLEncoder.encode(filename, "UTF-8").replace("+", "%20"); |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); |
| | | response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedName); |
| | |
| | | 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; |
| | | } |
| | | |
| | | //批量上传 |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void wordToPdf(String path, String sealUrl, boolean isRawMater) { |
| | | public void wordToPdf(String path, String maSealUrl, String seamSealUrl, boolean isRawMater) { |
| | | try { |
| | | wordToPdf(path, path.replace(".docx", ".pdf"), sealUrl, isRawMater); |
| | | wordToPdf(path, path.replace(".docx", ".pdf"), maSealUrl, seamSealUrl, isRawMater); |
| | | } catch (Exception e) { |
| | | throw new ErrorException("转换失败"); |
| | | } |
| | | } |
| | | |
| | | |
| | | public String wordToPdf(String wordPath, String pdfPath, String sealUrl, boolean isRawMater) { |
| | | public String wordToPdf(String wordPath, String pdfPath, String maSealUrl, |
| | | String seamSealUrl, boolean isRawMater) { |
| | | FileOutputStream os = null; |
| | | try { |
| | | //凭证 不然切换后有水印 |
| | |
| | | log.info("License验证不通过..."); |
| | | return null; |
| | | } |
| | | //生成一个空的PDF文件 |
| | | File file; |
| | | //判断是否是进厂报告 |
| | | if (!isRawMater) { |
| | | file = new File(pdfPath.replace(".pdf", "-1.pdf")); |
| | | } else { |
| | | file = new File(pdfPath.replace(".pdf", ".pdf")); |
| | | } |
| | | String effectiveSeamSealUrl = isRawMater ? null : seamSealUrl; |
| | | boolean addPdfMarks = StringUtils.isNotBlank(maSealUrl) |
| | | || StringUtils.isNotBlank(effectiveSeamSealUrl); |
| | | String sourcePdfPath = addPdfMarks ? pdfPath.replace(".pdf", "-stamp-source.pdf") : pdfPath; |
| | | File file = new File(sourcePdfPath); |
| | | os = new FileOutputStream(file); |
| | | //要转换的word文件 |
| | | com.aspose.words.Document doc = new com.aspose.words.Document(wordPath); |
| | | doc.save(os, SaveFormat.PDF); |
| | | os.close(); |
| | | os = null; |
| | | |
| | | //添加骑缝章 |
| | | if (!isRawMater) { |
| | | stamperCheckMarkPDF(pdfPath.replace(".pdf", "-1.pdf"), pdfPath, imgUrl + "/" + sealUrl); |
| | | // MA章固定在第1页左上角;骑缝章按页切片固定在右侧中部。 |
| | | if (addPdfMarks) { |
| | | stamperReportMarksPDF( |
| | | sourcePdfPath, |
| | | pdfPath, |
| | | StringUtils.isBlank(maSealUrl) ? null : Paths.get(imgUrl, maSealUrl).toString(), |
| | | StringUtils.isBlank(effectiveSeamSealUrl) |
| | | ? null : Paths.get(imgUrl, effectiveSeamSealUrl).toString()); |
| | | } |
| | | |
| | | |
| | | return new File(pdfPath).getName(); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | log.error("生成盖章PDF失败: {}", wordPath, e); |
| | | throw new ErrorException("报告转PDF或盖章失败"); |
| | | } finally { |
| | | if (os != null) { |
| | | try { |
| | |
| | | } |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | |
| | | subImg = img.getSubimage(i * sw, 0, sw, h); |
| | | } |
| | | ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| | | ImageIO.write(subImg, Path.substring(Path.lastIndexOf('.') + 1), out); |
| | | // 统一编码为 PNG,兼容历史“_png”等没有标准扩展名的图片文件。 |
| | | ImageIO.write(subImg, "png", out); |
| | | nImage[i] = com.itextpdf.text.Image.getInstance(out.toByteArray()); |
| | | |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * 盖骑缝章 |
| | | * 按参考报告盖 PDF 标志章:MA章位于第1页左上角,骑缝章位于每页右侧中部。 |
| | | * |
| | | * @param infilePath 原PDF路径 |
| | | * @param outFilePath 输出PDF路径 |
| | | */ |
| | | public static void stamperCheckMarkPDF(String infilePath, String outFilePath, String picPath) throws IOException, DocumentException { |
| | | public static void stamperReportMarksPDF(String infilePath, String outFilePath, |
| | | String maPicPath, String seamPicPath) |
| | | throws IOException, DocumentException { |
| | | PdfReader reader = new PdfReader(infilePath);//选择需要印章的pdf |
| | | PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(outFilePath));//加完印章后的pdf |
| | | |
| | | |
| | | com.itextpdf.text.Rectangle pageSize = reader.getPageSize(1);//获得第一页 |
| | | float height = pageSize.getHeight(); |
| | | float width = pageSize.getWidth(); |
| | | |
| | | int nums = reader.getNumberOfPages(); |
| | | com.itextpdf.text.Image[] nImage = slicingImages(picPath, nums);//生成骑缝章切割图片 |
| | | boolean hasMaSeal = maPicPath != null && !maPicPath.trim().isEmpty(); |
| | | boolean hasSeamSeal = seamPicPath != null && !seamPicPath.trim().isEmpty(); |
| | | com.itextpdf.text.Image[] seamImages = hasSeamSeal ? slicingImages(seamPicPath, nums) : null; |
| | | |
| | | for (int n = 1; n <= nums; n++) { |
| | | com.itextpdf.text.Rectangle pageSize = reader.getPageSize(n); |
| | | float height = pageSize.getHeight(); |
| | | float width = pageSize.getWidth(); |
| | | PdfContentByte over = stamp.getOverContent(n);//设置在第几页打印印章 |
| | | com.itextpdf.text.Image img = nImage[n - 1];//选择图片 |
| | | float newHeight = 100f; |
| | | float newWidth = img.getWidth() / (img.getHeight() / 100); |
| | | img.scaleAbsolute(newWidth, newHeight);//控制图片大小 |
| | | img.setAbsolutePosition(width - newWidth, height / 2 - newHeight / 2);//控制图片位置 |
| | | over.addImage(img); |
| | | |
| | | if (n == 1 && hasMaSeal) { |
| | | com.itextpdf.text.Image maImage = com.itextpdf.text.Image.getInstance(maPicPath); |
| | | maImage.scaleToFit(100f, 90f); |
| | | maImage.setAbsolutePosition(30f, height - maImage.getScaledHeight() - 30f); |
| | | over.addImage(maImage); |
| | | } |
| | | |
| | | if (hasSeamSeal) { |
| | | com.itextpdf.text.Image seamImage = seamImages[n - 1]; |
| | | float newHeight = 100f; |
| | | float newWidth = seamImage.getWidth() / (seamImage.getHeight() / newHeight); |
| | | seamImage.scaleAbsolute(newWidth, newHeight); |
| | | seamImage.setAbsolutePosition(width - newWidth, height / 2 - newHeight / 2); |
| | | over.addImage(seamImage); |
| | | } |
| | | } |
| | | stamp.close(); |
| | | reader.close(); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return unqualifiedCount; |
| | | } |
| | | |
| | | /** |
| | | * 退回到检验任务 |
| | | * @param id |
| | | * @return |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean sendBackTask(Integer id) { |
| | | InsReport report = insReportMapper.selectById(id); |
| | | // 根据订单查询试验室 |
| | | String laboratory = insOrderMapper.selectLaboratoryByOrderId(report.getInsOrderId()); |
| | | 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("报告不存在或未关联检验单"); |
| | | } |
| | | 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()) |
| | | .eq(InsOrderState::getLaboratory, laboratory) |
| | | .set(InsOrderState::getInsState, 4)); |
| | | .set(InsOrderState::getInsState, 4) |
| | | .set(InsOrderState::getVerifyUser, null) |
| | | .set(InsOrderState::getVerifyTell, reason)); |
| | | |
| | | Integer insSampleId = insSampleUserMapper.selectOne(Wrappers.<InsSampleUser>lambdaQuery() |
| | | // 只撤销复核人任务,保留各检验室原检验人,避免退回后任务无人可见。 |
| | | insSampleUserMapper.delete(Wrappers.<InsSampleUser>lambdaQuery() |
| | | .eq(InsSampleUser::getInsSampleId, report.getInsOrderId()) |
| | | .orderByDesc(InsSampleUser::getId) |
| | | .last("limit 1")).getId(); |
| | | insSampleUserMapper.deleteById(insSampleId); |
| | | .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())); |
| | | } |
| | | |
| | | /** |