inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
@@ -49,6 +49,7 @@
import com.ruoyi.inspect.util.ReportSignaturePictureUtils;
import com.ruoyi.inspect.vo.InsOrderPlanTaskSwitchVo;
import com.ruoyi.inspect.vo.InsOrderPlanVO;
import com.ruoyi.inspect.vo.QualityMonitorRawContext;
import com.ruoyi.inspect.vo.InsSampleUserVO;
import com.ruoyi.performance.mapper.AuxiliaryOutputWorkingHoursMapper;
import com.ruoyi.performance.pojo.AuxiliaryOutputWorkingHours;
@@ -223,6 +224,10 @@
    public boolean claimInsOrderPlan(InsOrderPlanDTO entity) {
        if (Objects.isNull(entity)) {
            return false;
        }
        InsOrder claimOrder = insOrderMapper.selectById(entity.getInsSampleId());
        if (claimOrder != null && "QUALITY_MONITOR".equals(claimOrder.getTaskSource())) {
            throw new ErrorException("质量监控任务由计划指定检测员,不能认领");
        }
        Integer userId = SecurityUtils.getUserId().intValue();
        InsSampleUser insSampleUser = new InsSampleUser(entity.getInsSampleId().intValue(), userId, 0, entity.getSonLaboratory());
@@ -612,6 +617,16 @@
    public void saveTemplateContext(SaveTemplateContextDto dto) {
        if (dto.getOrderId() == null || dto.getSampleId() == null || dto.getTemplateId() == null) {
            throw new ErrorException("订单、样品和模板不能为空");
        }
        InsOrder sourceOrder = insOrderMapper.selectById(dto.getOrderId());
        if (sourceOrder != null && "QUALITY_MONITOR".equals(sourceOrder.getTaskSource())
                && !Objects.equals(sourceOrder.getState(), 1)) {
            throw new ErrorException("已取消的质量监控任务不能填写原始记录");
        }
        if (sourceOrder != null && "QUALITY_MONITOR".equals(sourceOrder.getTaskSource())
                && !Objects.equals(SecurityUtils.getUserId().intValue(),
                    insOrderMapper.selectQualityMonitorInspectorId(dto.getOrderId()))) {
            throw new ErrorException("只有计划指定的检测员可以填写质量监控原始记录");
        }
        InsSample sample = insSampleMapper.selectById(dto.getSampleId());
        if (sample == null || !Objects.equals(sample.getInsOrderId(), dto.getOrderId())) {
@@ -1027,6 +1042,10 @@
    //交接
    @Override
    public int upPlanUser(Integer userId, Integer orderId, String sonLaboratory) {
        InsOrder handoffOrder = insOrderMapper.selectById(orderId);
        if (handoffOrder != null && "QUALITY_MONITOR".equals(handoffOrder.getTaskSource())) {
            throw new ErrorException("质量监控任务检测员由计划指定,不能直接交接");
        }
        InsSampleUser insSampleUser = new InsSampleUser();
        insSampleUser.setUserId(userId);
        insSampleUser.setInsSampleId(orderId);
@@ -1211,6 +1230,23 @@
        InsOrder order = insOrderMapper.selectOne(Wrappers.<InsOrder>lambdaQuery()
                .eq(InsOrder::getId, orderId)
                .last("FOR UPDATE"));
        if (order == null) throw new ErrorException("检验任务不存在");
        if ("QUALITY_MONITOR".equals(order.getTaskSource()) && !Objects.equals(order.getState(), 1)) {
            throw new ErrorException("已取消的质量监控任务不能提交");
        }
        if ("QUALITY_MONITOR".equals(order.getTaskSource())
                && (verifyUser == null || userMapper.selectById(verifyUser) == null)) {
            throw new ErrorException("请选择质量监控任务复核人");
        }
        if ("QUALITY_MONITOR".equals(order.getTaskSource())
                && Objects.equals(verifyUser, insOrderMapper.selectQualityMonitorInspectorId(orderId))) {
            throw new ErrorException("检测员不能复核自己的质量监控原始记录");
        }
        if ("QUALITY_MONITOR".equals(order.getTaskSource())
                && !Objects.equals(SecurityUtils.getUserId().intValue(),
                    insOrderMapper.selectQualityMonitorInspectorId(orderId))) {
            throw new ErrorException("只有计划指定的检测员可以提交质量监控任务");
        }
        boolean isInspectionReport = Objects.equals(INSPECTION_REPORT, reportType);
        registerInsResults = isInspectionReport && Boolean.TRUE.equals(registerInsResults);
        // 1. 委托编号全局唯一,提交复核时再次校验,防止并发或历史数据绕过生成校验。
@@ -1675,11 +1711,25 @@
        if (orderId == null || laboratory == null || type == null) {
            throw new ErrorException("复核参数不完整");
        }
        InsOrder verifyOrder = insOrderMapper.selectById(orderId);
        if (verifyOrder == null) throw new ErrorException("检验任务不存在");
        if ("QUALITY_MONITOR".equals(verifyOrder.getTaskSource()) && !Objects.equals(verifyOrder.getState(), 1)) {
            throw new ErrorException("已取消的质量监控任务不能复核");
        }
        ensureDirectTemplateOrderState(orderId);
        InsOrderState currentState = insOrderStateMapper.selectOne(Wrappers.<InsOrderState>lambdaQuery()
                .eq(InsOrderState::getInsOrderId, orderId)
                .eq(InsOrderState::getLaboratory, laboratory)
                .last("limit 1"));
        if ("QUALITY_MONITOR".equals(verifyOrder.getTaskSource())
                && (currentState == null || currentState.getVerifyUser() == null
                    || !Objects.equals(SecurityUtils.getUserId().intValue(), currentState.getVerifyUser()))) {
            throw new ErrorException("只有提交时指定的复核人可以复核质量监控任务");
        }
        if ("QUALITY_MONITOR".equals(verifyOrder.getTaskSource()) && Objects.equals(type, 1)
                && currentState != null && Objects.equals(currentState.getInsState(), 5)) {
            return 1;
        }
        InsReport existingReport = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
                .eq(InsReport::getInsOrderId, orderId)
                .last("limit 1"));
@@ -1738,6 +1788,24 @@
            insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
                    .eq(InsOrder::getId, orderId)
                    .set(InsOrder::getInsState, 5));
            // 质量监控任务只保留原始记录,后续由质量监控评价完成归档。
            if ("QUALITY_MONITOR".equals(order.getTaskSource())) {
                Integer evaluatorId = insOrderMapper.selectQualityMonitorEvaluatorId(orderId);
                if (evaluatorId != null) {
                    InformationNotification notice = new InformationNotification();
                    notice.setCreateUser(insProductMapper.selectUserById(SecurityUtils.getUserId().intValue()).get("name"));
                    notice.setSenderId(SecurityUtils.getUserId().intValue());
                    notice.setConsigneeId(evaluatorId);
                    notice.setMessageType("2");
                    notice.setTheme("质量监控评价通知");
                    notice.setContent("质量监控任务 " + order.getEntrustCode() + " 的原始记录已复核,请填写评价记录");
                    notice.setViewStatus(false);
                    notice.setJumpPath(MenuJumpPathConstants.QUALITY_MONITOR);
                    informationNotificationService.addInformationNotification(notice);
                }
                return 1;
            }
            // 检验人员提交复核时不生成报告;全部试验室复核通过后生成报告。
            if (report == null || StringUtils.isBlank(report.getUrl())) {
@@ -2009,6 +2077,10 @@
     * @param orderId
     */
    private void generateReport(Integer orderId, Integer reportType, Integer writeUserId) {
        InsOrder sourceOrder = insOrderMapper.selectById(orderId);
        if (sourceOrder != null && "QUALITY_MONITOR".equals(sourceOrder.getTaskSource())) {
            throw new ErrorException("质量监控检验任务不生成报告");
        }
        /*样品下的项目只要有一个项目不合格则检验结果为0,否则为1*/
        //这里的insSamples是订单下的所有样品包括("/")
        List<InsSample> insSamples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery().eq(InsSample::getInsOrderId, orderId));
@@ -3774,6 +3846,40 @@
    /**
     * 新聚直绑模板订单:模板中每一个“导出值”批注都对应报告中的一条检测项目。
     */
    @Override
    public List<Map<String, String>> getQualityMonitorRawResults(Integer orderId) {
        requireReviewedQualityMonitorOrder(orderId);
        return buildTemplateDetectionResultRows(orderId);
    }
    @Override
    public QualityMonitorRawContext getQualityMonitorRawContext(Integer orderId) {
        requireReviewedQualityMonitorOrder(orderId);
        QualityMonitorRawContext context = new QualityMonitorRawContext();
        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
        context.setDetectionDates(snapshots.stream().map(InsOrderStandardTemplate::getDetectionTime)
                .filter(Objects::nonNull).distinct().sorted().collect(Collectors.toList()));
        List<Integer> deviceIds = insOrderTemplateDeviceMapper.selectDeviceIdsByOrderId(orderId);
        if (CollectionUtils.isNotEmpty(deviceIds)) {
            context.setDevices(insOrderMapper.selectDeviceListByIds(new LinkedHashSet<>(deviceIds)));
        }
        return context;
    }
    private void requireReviewedQualityMonitorOrder(Integer orderId) {
        InsOrder order = insOrderMapper.selectById(orderId);
        if (order == null || !"QUALITY_MONITOR".equals(order.getTaskSource())
                || !Objects.equals(order.getInsState(), 5)) {
            throw new ErrorException("质控任务原始记录复核通过后才能读取检测数据");
        }
        Long pendingCount = insOrderStateMapper.selectCount(Wrappers.<InsOrderState>lambdaQuery()
                .eq(InsOrderState::getInsOrderId, orderId).ne(InsOrderState::getInsState, 5));
        if (pendingCount > 0) throw new ErrorException("质控任务仍有未复核的原始记录");
    }
    private List<Map<String, String>> buildTemplateDetectionResultRows(Integer orderId) {
        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
                Wrappers.<InsOrderStandardTemplate>lambdaQuery()