inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
@@ -1,6 +1,5 @@
package com.ruoyi.inspect.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
@@ -11,7 +10,6 @@
import com.aspose.words.SaveFormat;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
@@ -19,21 +17,25 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.Pictures;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.ruoyi.basic.dto.IfsInventoryQuantitySupplierDto;
import com.ruoyi.basic.mapper.IfsInventoryQuantityMapper;
import com.ruoyi.basic.pojo.IfsInventoryQuantity;
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.InformationNotification;
import com.ruoyi.common.config.WechatProperty;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.DateImageUtil;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.WxCpUtils;
import com.ruoyi.common.utils.api.IfsApiUtils;
import com.ruoyi.framework.exception.ErrorException;
import com.ruoyi.inspect.dto.InsReportExport;
@@ -42,15 +44,18 @@
import com.ruoyi.inspect.pojo.*;
import com.ruoyi.inspect.service.InsOrderService;
import com.ruoyi.inspect.service.InsReportService;
import com.ruoyi.inspect.mapper.InsUnqualifiedHandlerMapper;
import com.ruoyi.inspect.service.RawMaterialOrderService;
import com.ruoyi.inspect.util.PreserveReportPicturePlaceholderHandler;
import com.ruoyi.system.mapper.UserMapper;
import com.ruoyi.system.service.InformationNotificationService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@@ -81,6 +86,9 @@
@Slf4j
public class InsReportServiceImpl extends ServiceImpl<InsReportMapper, InsReport>
        implements InsReportService {
    private static final int DETECTION_REPORT = 0;
    private static final int INSPECTION_REPORT = 1;
    @Resource
    private UserMapper userMapper;
    @Resource
@@ -116,6 +124,8 @@
    private IfsApiUtils ifsApiUtils;
    @Resource
    private InsSampleUserMapper insSampleUserMapper;
    @Autowired
    private RawMaterialOrderService rawMaterialOrderService;
    @Override
@@ -202,30 +212,30 @@
        info.setViewStatus(false);
        info.setJumpPath(MenuJumpPathConstants.REPORT_PREPARATION);
        informationNotificationService.addInformationNotification(info);
        //系统生成报告地址
        String url = insReport.getUrl();
        //手动上传报告地址
        String urlS = insReport.getUrlS();
        String primaryReportUrl = getPrimaryReportUrl(insReport);
        // 判断是否是原材料  需要替换****成供应商
        IfsInventoryQuantity one = ifsInventoryQuantityMapper.selectOne(new LambdaQueryWrapper<IfsInventoryQuantity>()
                .eq(IfsInventoryQuantity::getId, order.getIfsInventoryId()));
        if (one != null) {
            if (isRawMater && order.getOrderType().equals(InsOrderTypeConstants.ENTER_THE_FACTORY)) {
                changeText(new HashMap<String, String>() {{
                    put("**********", one.getSupplierName());
                }}, (StrUtil.isBlank(urlS) ? url : urlS).replace("/word", wordUrl));
                for (String reportUrl : getApprovalReportUrls(insReport)) {
                    changeText(new HashMap<String, String>() {{
                        put("**********", one.getSupplierName());
                    }}, reportUrl.replace("/word", wordUrl));
                }
            }
        }
        wordInsertUrl(new HashMap<String, Object>() {{
        Map<String, Object> writeMarks = new HashMap<String, Object>() {{
            put("writeUrl", Pictures.ofLocal(imgUrl + "/" + signatureUrl).create());
            put("writeDateUrl", Pictures.ofStream(DateImageUtil.createDateImage(null)).create());
            put("insUrl", Pictures.ofLocal(imgUrl + "/" + signatureUrl).create());
        }}, (StrUtil.isBlank(urlS) ? url : urlS).replace("/word", wordUrl));
        }};
        writeMarksToReportFiles(insReport, writeMarks);
        // 修改临时pdf
        String tempUrlPdf = wordToPdfTemp((StrUtil.isBlank(urlS) ? url : urlS).replace("/word", wordUrl));
        String tempUrlPdf = wordToPdfTemp(primaryReportUrl.replace("/word", wordUrl));
        insReport.setTempUrlPdf("/word/" + tempUrlPdf);
        insReportMapper.updateById(insReport);
@@ -323,17 +333,15 @@
        info.setViewStatus(false);
        info.setJumpPath(MenuJumpPathConstants.REPORT_PREPARATION);
        informationNotificationService.addInformationNotification(info);
        //系统生成报告地址
        String url = insReport.getUrl();
        //手动上传报告地址
        String urlS = insReport.getUrlS();
        wordInsertUrl(new HashMap<String, Object>() {{
        String primaryReportUrl = getPrimaryReportUrl(insReport);
        Map<String, Object> examineMarks = new HashMap<String, Object>() {{
            put("examineUrl", Pictures.ofLocal(imgUrl + "/" + signatureUrl).create());
            put("examineDateUrl", Pictures.ofStream(DateImageUtil.createDateImage(null)).create());
        }}, (StrUtil.isBlank(urlS) ? url : urlS).replace("/word", wordUrl));
        }};
        writeMarksToReportFiles(insReport, examineMarks);
        // 修改临时pdf
        String tempUrlPdf = wordToPdfTemp((StrUtil.isBlank(urlS) ? url : urlS).replace("/word", wordUrl));
        String tempUrlPdf = wordToPdfTemp(primaryReportUrl.replace("/word", wordUrl));
        insReport.setTempUrlPdf("/word/" + tempUrlPdf);
        // 发送企业微信通知(通知批准人审批)
@@ -466,28 +474,29 @@
        if (StringUtils.isBlank(sealUrl)) {
            throw new ErrorException(laboratory + "找不到报告专用章");
        }
        //系统生成报告地址
        String url = insReport.getUrl();
        //手动上传报告地址
        String urlS = insReport.getUrlS();
        String finalUrl = (StrUtil.isBlank(urlS) ? url : urlS).replace("/word", wordUrl);
        String primaryReportUrl = getPrimaryReportUrl(insReport);
        String finalUrl = primaryReportUrl.replace("/word", wordUrl);
        wordInsertUrl(new HashMap<String, Object>() {{
        Map<String, Object> ratifyMarks = new HashMap<String, Object>() {{
            put("ratifyUrl", Pictures.ofLocal(imgUrl + "/" + signatureUrl).create());
            put("ratifyDateUrl", Pictures.ofStream(DateImageUtil.createDateImage(null)).create());
            put("seal1", Pictures.ofLocal(imgUrl + "/" + sealUrl).create());
            put("seal2", Pictures.ofLocal(imgUrl + "/" + sealUrl).create());
        }}, finalUrl);
        }};
        writeMarksToReportFiles(insReport, ratifyMarks);
        // 修改临时pdf
        insReport.setTempUrlPdf((StrUtil.isBlank(urlS) ? url : urlS).replace(".docx", ".pdf"));
        insReport.setTempUrlPdf(primaryReportUrl.replace(".docx", ".pdf"));
        InsOrder insOrder = new InsOrder();
        insOrder.setId(insOrderId);
        insOrder.setState(4);
        insOrderMapper.updateById(insOrder);
        wordToPdf(finalUrl, sealUrl, isRawMater && order.getOrderType().equals(InsOrderTypeConstants.ENTER_THE_FACTORY));
        for (String reportUrl : getApprovalReportUrls(insReport)) {
            wordToPdf(reportUrl.replace("/word", wordUrl), sealUrl,
                    isRawMater && order.getOrderType().equals(InsOrderTypeConstants.ENTER_THE_FACTORY));
        }
        // 判断是否为原材料
        if (isRawMater) {
@@ -518,13 +527,37 @@
    @Override
    public int wordInsertUrl(Map<String, Object> map, String url) {
        XWPFTemplate template = XWPFTemplate.compile(url).render(map);
        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 String getPrimaryReportUrl(InsReport report) {
        String reportUrl = StringUtils.isNotBlank(report.getUrlS()) ? report.getUrlS() : report.getUrl();
        if (StringUtils.isBlank(reportUrl)) {
            throw new ErrorException("检测报告文件不存在");
        }
        return reportUrl;
    }
    private List<String> getApprovalReportUrls(InsReport report) {
        return Arrays.asList(getPrimaryReportUrl(report), report.getInspectionUrl()).stream()
                .filter(StringUtils::isNotBlank)
                .distinct()
                .collect(Collectors.toList());
    }
    private void writeMarksToReportFiles(InsReport report, Map<String, Object> marks) {
        for (String reportUrl : getApprovalReportUrls(report)) {
            wordInsertUrl(marks, reportUrl.replace("/word", wordUrl));
        }
    }
@@ -564,6 +597,53 @@
            e.printStackTrace();
        }
        return "/word/zip/output.zip";
    }
    @Override
    public void download(Integer id, Integer reportType, Integer createOrderUser, HttpServletResponse response) {
        if (id == null) {
            throw new ErrorException("报告不能为空");
        }
        reportType = reportType == null ? DETECTION_REPORT : reportType;
        if (!Objects.equals(reportType, DETECTION_REPORT) && !Objects.equals(reportType, INSPECTION_REPORT)) {
            throw new ErrorException("报告类型不正确");
        }
        InsReport report = insReportMapper.selectDownloadableReport(id, createOrderUser);
        if (report == null) {
            throw new ErrorException("报告不存在、尚未生成或无权下载");
        }
        String reportUrl = Objects.equals(reportType, INSPECTION_REPORT)
                ? report.getInspectionUrl() : getPrimaryReportUrl(report);
        if (StringUtils.isBlank(reportUrl) || !reportUrl.replace('\\', '/').startsWith("/word/")) {
            throw new ErrorException((Objects.equals(reportType, INSPECTION_REPORT) ? "检验报告" : "检测报告")
                    + "文件不存在,请联系管理员");
        }
        Path root = Paths.get(wordUrl).toAbsolutePath().normalize();
        Path file = root.resolve(reportUrl.replace('\\', '/').substring("/word/".length())).normalize();
        if (!file.startsWith(root) || !Files.isRegularFile(file)) {
            throw new ErrorException("报告文件不存在,请联系管理员");
        }
        try {
            String filename = file.getFileName().toString();
            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);
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
            response.setHeader("download-filename", encodedName);
            response.setContentLengthLong(Files.size(file));
            try (InputStream inputStream = Files.newInputStream(file);
                 OutputStream outputStream = response.getOutputStream()) {
                byte[] buffer = new byte[8192];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, length);
                }
                outputStream.flush();
            }
        } catch (IOException e) {
            log.error("下载报告失败,reportId={}", id, e);
            throw new ErrorException("报告下载失败");
        }
    }
    //批量上传
@@ -793,8 +873,9 @@
        stamp.close();
    }
    @Transactional(rollbackFor = Exception.class)
    public void isRawMaterial(InsOrder insOrder) {
    @Override
    @Transactional(rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
    public void isRawMaterial(InsOrder insOrder,Boolean registerInsResults,Boolean hasExemption) {
        IfsInventoryQuantity one = ifsInventoryQuantityMapper.selectOne(new LambdaQueryWrapper<IfsInventoryQuantity>()
                .eq(IfsInventoryQuantity::getId, insOrder.getIfsInventoryId()));
        if (Objects.isNull(one)) {
@@ -808,9 +889,26 @@
        // 判断是否有不合格
        Long unqualifiedCount = getUnqualifiedCount(insOrder);
        if (count.equals(0L) && unqualifiedCount.equals(0L) && one.getIsFinish().equals(0) && one.getIsSource().equals(1)) {
        if (count.equals(0L) && unqualifiedCount.equals(0L) && one.getIsFinish().equals(0)
                && one.getIsSource().equals(1) && Boolean.TRUE.equals(registerInsResults)) {
            // 原材料移库
            toLocation = this.moveRawMaterial(one);
            //如果是拆分的订单,则把拆分的所有批次都移库
            if(one.getIsSplitOrder()==1 && !hasExemption){
                //查询拆分订单详情
                List<IfsInventoryQuantityVO> ifsInventoryQuantityVOS = ifsInventoryQuantityMapper.selectSplitOrderList(one.getPartNo(), one.getLineNo(), one.getReleaseNo(), one.getReceiptNo(), one.getOrderNo());
                for (IfsInventoryQuantityVO vo : ifsInventoryQuantityVOS) {
                    //如果有委托单信息且合格,走登记后移库;如果没有,则走免检;不合格不做处理
                    if(Objects.isNull(vo.getInsOrderId())){
                        rawMaterialOrderService.rawOrderRelease(vo.getId(), vo.getPartDesc());
                    }else if(Objects.equals(vo.getInsOrderId(),insOrder.getId())){
                        toLocation = insOrderService.moveRawMaterial(vo);
                    }else if(Objects.nonNull(vo.getInsResult()) && 1 == vo.getInsResult()){
                        toLocation = insOrderService.moveRawMaterial(vo);
                    }
                }
            }else{
                toLocation = insOrderService.moveRawMaterial(one);
            }
        }
        // 判断结束状态修改合格状态
@@ -895,75 +993,6 @@
            }
        }
        return unqualifiedCount;
    }
    /**
     * ifs移库操作
     * @param one
     * @return
     */
    @Override
    public String moveRawMaterial(IfsInventoryQuantity one) {
        String toLocation;
        // 登记采购检验结果STD
        if (one.getIsRegister().equals(0)) {
            Map<String, Object> resultMap = new HashMap<>();
            List<Map<String, Object>> resultList = new ArrayList<>();
            Map<String, Object> map = new HashMap<>();
            map.put("ORDER_NO", one.getOrderNo()); // 采购订单号
            map.put("LINE_NO", one.getLineNo()); // 行号
            map.put("RELEASE_NO", one.getReleaseNo()); // 下达号
            map.put("RECEIPT_NO", one.getReceiptNo()); // 接收号
            map.put("PURCH_QTY", one.getQtyToInspect()); // 要检验的采购数量
            resultList.add(map);
            resultMap.put("RECORD_ID", UUID.randomUUID().toString());
            resultMap.put("SYSCODE", "LIMS");
            resultMap.put("SYSMODEL", "登记采购检验结果");
            resultMap.put("BATCH_INFO", resultList);
            Result result = ifsApiUtils.getProcurementResults(JSONUtil.toJsonStr(resultMap));
            if (result.getCode() != 200) {
                throw new ErrorException("IFS登记采购检验结果失败: " + result.getMessage());
            }
        }
        insOrderService.updateIfsInventoryQuantity(one.getId());
        /**
         * TODO 后续需要调用IFS的接口 移入的库位号 toLocation
         */
        // 检验后移库
        toLocation = "1301";
        Map<String, Object> moveResultMap = new HashMap<>();
        List<Map<String, Object>> moveResultList = new ArrayList<>();
        Map<String, Object> moveMap = new HashMap<>();
        moveMap.put("ORDER_NO", one.getOrderNo()); // 采购订单号
        moveMap.put("LINE_NO", one.getLineNo());
        moveMap.put("RELEASE_NO", one.getReleaseNo());
        moveMap.put("RECEIPT_NO", one.getReceiptNo());
        moveMap.put("PART_NO", one.getPartNo());
        moveMap.put("QTY", one.getQtyArrived());
        moveMap.put("LOCATION_NO", one.getLocationNo());
        moveMap.put("TO_LOCATION_NO", toLocation);
        moveMap.put("LOT_BATCH_NO", one.getLotBatchNo());
        moveMap.put("SERIAL_NO", one.getSerialNo());
        moveMap.put("WAIV_DEV_REJ_NO", one.getWaivDevRejNo());
        moveMap.put("ENG_CHG_LEVEL", one.getEngChgLevel());
        moveMap.put("ACTIVITY_SEQ", one.getActivitySeq());
        moveResultList.add(moveMap);
        moveResultMap.put("RECORD_ID", UUID.randomUUID().toString());
        moveResultMap.put("SYSCODE", "LIMS");
        moveResultMap.put("SYSMODEL", "检验后移库");
        moveResultMap.put("BATCH_INFO", moveResultList);
        Result result1 = ifsApiUtils.moveReceipt(JSONUtil.toJsonStr(moveResultMap));
        // 如果有必须为零件指定批号报错需要重新提交移库信息去指定批号
        if (result1.getCode() != 200) {
            String message = result1.getMessage();
            if (message.contains("必须为零件") && message.contains("指定批号")) {
                updaeBatch(one, toLocation);
            } else {
                throw new ErrorException("IFS检验后移库失败: " + result1.getMessage());
            }
        }
        return toLocation;
    }
    /**