inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java
@@ -125,8 +125,32 @@
        }
        // 检验中:新聚下单免审核即受理,需样品接收提交后(ins_sample_receipt.status=1)才展示
        String receiptSubmitted = sampleOrderDto.getState() != null && sampleOrderDto.getState() == 1 ? "1" : null;
        return insOrderMapper.selectInsOrderPage(page, QueryWrappers.queryWrappers(sampleOrderDto), laboratory,
                isOrderAll, receiptSubmitted);
        IPage<SampleOrderDto> result = insOrderMapper.selectInsOrderPage(page,
                QueryWrappers.queryWrappers(sampleOrderDto), laboratory, isOrderAll, receiptSubmitted);
        List<Integer> candidateIds = result.getRecords().stream()
                .filter(row -> Integer.valueOf(-1).equals(row.getTypeSource())
                        && Integer.valueOf(1).equals(row.getState()))
                .map(SampleOrderDto::getId)
                .collect(Collectors.toList());
        if (!candidateIds.isEmpty()) {
            Set<Integer> receivedIds = insSampleReceiptMapper.selectList(Wrappers.<InsSampleReceipt>lambdaQuery()
                            .in(InsSampleReceipt::getInsOrderId, candidateIds)
                            .eq(InsSampleReceipt::getStatus, 1)
                            .select(InsSampleReceipt::getInsOrderId)).stream()
                    .map(InsSampleReceipt::getInsOrderId).collect(Collectors.toSet());
            Set<Integer> taskIds = insOrderStateService.list(Wrappers.<InsOrderState>lambdaQuery()
                            .in(InsOrderState::getInsOrderId, candidateIds)
                            .select(InsOrderState::getInsOrderId)).stream()
                    .map(InsOrderState::getInsOrderId).collect(Collectors.toSet());
            Set<Integer> assignedIds = insSampleUserMapper.selectList(Wrappers.<InsSampleUser>lambdaQuery()
                            .in(InsSampleUser::getInsSampleId, candidateIds)
                            .select(InsSampleUser::getInsSampleId)).stream()
                    .map(InsSampleUser::getInsSampleId).collect(Collectors.toSet());
            result.getRecords().forEach(row -> row.setEditable(
                    candidateIds.contains(row.getId()) && !receivedIds.contains(row.getId())
                            && !taskIds.contains(row.getId()) && !assignedIds.contains(row.getId())));
        }
        return result;
    }
@@ -487,6 +511,9 @@
        }
        // 委托要求字段校验:四组单选必选,分包/快递联动必填
        InsOrder reqOrder = dto.getInsOrder();
        if (reqOrder.getSampleDate() == null) {
            throw new ErrorException("送样日期不能为空");
        }
        if (reqOrder.getSubcontractType() == null || reqOrder.getSampleDisposal() == null
                || reqOrder.getResultEvaluation() == null || reqOrder.getReportDelivery() == null) {
            throw new ErrorException("请选择分包内容、样品处置、对结果评价和报告交付方式");
@@ -633,6 +660,170 @@
        return order.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateNewJuInsOrder(InsPlaceOrderDto dto) {
        if (dto == null || dto.getOrderId() == null || dto.getInsOrder() == null || dto.getStandardTreeId() == null) {
            throw new ErrorException("下单信息或标准不能为空");
        }
        InsOrder currentOrder = insOrderMapper.selectById(dto.getOrderId());
        if (currentOrder == null) {
            throw new ErrorException("检验单不存在");
        }
        if (!Integer.valueOf(1).equals(currentOrder.getState()) || !Integer.valueOf(-1).equals(currentOrder.getTypeSource())) {
            throw new ErrorException("当前委托不允许编辑");
        }
        assertNewJuOrderEditable(dto.getOrderId());
        String sampleCode = StringUtils.trim(dto.getSampleCode());
        if (StringUtils.isBlank(sampleCode) || sampleCode.length() < 2) {
            throw new ErrorException("样品编号不能为空且不能少于2位");
        }
        InsOrder order = dto.getInsOrder();
        if (order.getSampleDate() == null) {
            throw new ErrorException("送样日期不能为空");
        }
        if (order.getSubcontractType() == null || order.getSampleDisposal() == null
                || order.getResultEvaluation() == null || order.getReportDelivery() == null) {
            throw new ErrorException("请选择分包内容、样品处置、对结果评价和报告交付方式");
        }
        if (Integer.valueOf(1).equals(order.getSubcontractType()) && StringUtils.isBlank(order.getSubcontractCompany())) {
            throw new ErrorException("分包内容为有时,分包单位信息不能为空");
        }
        if (Integer.valueOf(2).equals(order.getReportDelivery())
                && (StringUtils.isBlank(order.getReportAddress()) || StringUtils.isBlank(order.getReportReceiver()))) {
            throw new ErrorException("报告交付方式为快递时,请填写报告寄送地址和收件人及电话");
        }
        StandardTree standard = standardTreeService.getById(dto.getStandardTreeId());
        if (standard == null || StringUtils.isBlank(standard.getSample())
                || standard.getSampleType() != null || standard.getModel() != null) {
            throw new ErrorException("请选择标准库二级标准");
        }
        List<StandardTreeTemplateBindingVo> bindings = standardTreeService.selectTemplateBindings(dto.getStandardTreeId());
        if (bindings.isEmpty()) {
            throw new ErrorException("该标准尚未绑定可用的原始记录模板");
        }
        Map<Integer, StandardTreeTemplateBindingVo> bindingMap = bindings.stream()
                .collect(Collectors.toMap(StandardTreeTemplateBindingVo::getTemplateId, binding -> binding, (first, ignored) -> first));
        List<OrderTemplateSelectionDto> selections = dto.getTemplateList();
        if (selections == null) {
            selections = bindings.stream().map(binding -> {
                OrderTemplateSelectionDto selection = new OrderTemplateSelectionDto();
                selection.setTemplateId(binding.getTemplateId());
                selection.setSort(binding.getSort());
                return selection;
            }).collect(Collectors.toList());
        }
        if (selections.isEmpty()) {
            throw new ErrorException("请至少保留一个原始记录模板");
        }
        Set<Integer> selectedTemplateIds = new HashSet<>();
        for (int index = 0; index < selections.size(); index++) {
            OrderTemplateSelectionDto selection = selections.get(index);
            if (selection == null || selection.getTemplateId() == null || !bindingMap.containsKey(selection.getTemplateId())) {
                throw new ErrorException("所选原始记录模板不属于当前标准");
            }
            if (!selectedTemplateIds.add(selection.getTemplateId())) {
                throw new ErrorException("原始记录模板不能重复选择");
            }
            if (selection.getSort() == null) {
                selection.setSort(index);
            }
        }
        selections.sort(Comparator.comparing(OrderTemplateSelectionDto::getSort)
                .thenComparing(OrderTemplateSelectionDto::getTemplateId));
        Map<Integer, Integer> templateSortMap = selections.stream().collect(Collectors.toMap(
                OrderTemplateSelectionDto::getTemplateId, OrderTemplateSelectionDto::getSort));
        List<TemplateInspectionItemDto> templateItems = standardTreeService.selectTemplateInspectionItems(dto.getStandardTreeId()).stream()
                .filter(item -> selectedTemplateIds.contains(item.getTemplateId()))
                .sorted(Comparator.comparing((TemplateInspectionItemDto item) -> templateSortMap.get(item.getTemplateId()))
                        .thenComparing(item -> item.getTemplateRowIndex() == null ? Integer.MAX_VALUE : item.getTemplateRowIndex()))
                .collect(Collectors.toList());
        StandardMethod standardMethod = standardMethodService.getById(standard.getStandardMethodId());
        if (standardMethod == null || StringUtils.isBlank(standardMethod.getCode())) {
            throw new ErrorException("标准编号不存在");
        }
        String standardCode = standardMethod.getCode().trim();
        order.setId(currentOrder.getId());
        order.setState(currentOrder.getState());
        order.setInsState(currentOrder.getInsState());
        order.setExamineTime(currentOrder.getExamineTime());
        order.setSendTime(currentOrder.getSendTime());
        order.setIsExemption(currentOrder.getIsExemption());
        order.setFactory(null);
        order.setLaboratory(standard.getLaboratory());
        order.setSampleType(null);
        order.setSample(standardCode);
        order.setPrepareUserEn(null);
        order.setPrepareCode(null);
        order.setRemarkEn(null);
        order.setTemperature(null);
        order.setHumidity(null);
        if (!Integer.valueOf(1).equals(order.getSubcontractType())) {
            order.setSubcontractCompany(null);
        }
        if (!Integer.valueOf(2).equals(order.getReportDelivery())) {
            order.setReportAddress(null);
            order.setReportReceiver(null);
        }
        insOrderMapper.updateById(order);
        List<InsSample> samples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery()
                .eq(InsSample::getInsOrderId, currentOrder.getId()).orderByAsc(InsSample::getId));
        if (samples.size() != 1) {
            throw new ErrorException("检验样品数据异常");
        }
        InsSample sample = samples.get(0);
        sample.setSampleCode(sampleCode);
        sample.setLaboratory(standard.getLaboratory());
        sample.setSampleType(null);
        sample.setSample(standardCode);
        sample.setModel(null);
        insSampleMapper.updateById(sample);
        insProductMapper.delete(Wrappers.<InsProduct>lambdaQuery().eq(InsProduct::getInsSampleId, sample.getId()));
        insOrderStandardTemplateMapper.delete(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
                .eq(InsOrderStandardTemplate::getInsOrderId, currentOrder.getId()));
        insOrderTemplateDeviceMapper.delete(Wrappers.<InsOrderTemplateDevice>lambdaQuery()
                .eq(InsOrderTemplateDevice::getInsOrderId, currentOrder.getId()));
        int productSort = 0;
        for (TemplateInspectionItemDto item : templateItems) {
            InsProduct product = new InsProduct();
            product.setInsSampleId(sample.getId());
            product.setInspectionItem(item.getInspectionItem());
            product.setInspectionItemClass("");
            product.setInspectionItemSubclass("");
            product.setLaboratory(standard.getLaboratory());
            product.setSample(standardCode);
            product.setTemplateId(item.getTemplateId());
            product.setTemplateRowIndex(item.getTemplateRowIndex());
            product.setSonLaboratory(DIRECT_TEMPLATE_LABORATORY);
            product.setState(1);
            product.setSort(productSort++);
            insProductMapper.insert(product);
        }
        saveTemplateSnapshots(currentOrder.getId(), selections, bindingMap);
        String entrustCode = buildEntrustCode(order, Collections.singletonList(sample));
        assertEntrustCodeUnique(entrustCode, currentOrder.getId());
        insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate().eq(InsOrder::getId, currentOrder.getId())
                .set(InsOrder::getEntrustCode, entrustCode));
        applicationEventPublisher.publishEvent(new com.ruoyi.inspect.event.NewJuOrderPlacedEvent(currentOrder.getId()));
        return currentOrder.getId();
    }
    private void assertNewJuOrderEditable(Integer orderId) {
        Long receiptCount = insSampleReceiptMapper.selectCount(Wrappers.<InsSampleReceipt>lambdaQuery()
                .eq(InsSampleReceipt::getInsOrderId, orderId).eq(InsSampleReceipt::getStatus, 1));
        Long taskCount = insOrderStateService.count(Wrappers.<InsOrderState>lambdaQuery()
                .eq(InsOrderState::getInsOrderId, orderId));
        Long userCount = insSampleUserMapper.selectCount(Wrappers.<InsSampleUser>lambdaQuery()
                .eq(InsSampleUser::getInsSampleId, orderId));
        if (receiptCount > 0 || taskCount > 0 || userCount > 0) {
            throw new ErrorException("该委托已进入检验,不能编辑");
        }
    }
    private void saveTemplateSnapshots(Integer orderId, List<OrderTemplateSelectionDto> selections,
                                       Map<Integer, StandardTreeTemplateBindingVo> bindingMap) {
        for (OrderTemplateSelectionDto selection : selections) {
@@ -732,6 +923,36 @@
        map.put("sampleCode", orderSamples.isEmpty() ? null : orderSamples.get(0).getSampleCode());
        map.put("insOrderTemplate", map1);
        map.put("templateList", templateList);
        Set<Integer> orderTemplateIds = templateList.stream()
                .map(OrderTemplateSummaryVo::getTemplateId).collect(Collectors.toSet());
        List<Integer> methodIds = standardMethodService.list(Wrappers.<StandardMethod>lambdaQuery()
                        .eq(StandardMethod::getCode, insOrder.getSample())
                        .select(StandardMethod::getId)).stream()
                .map(StandardMethod::getId).collect(Collectors.toList());
        if (!methodIds.isEmpty() && !orderTemplateIds.isEmpty()) {
            List<StandardTree> candidates = standardTreeService.list(Wrappers.<StandardTree>lambdaQuery()
                    .in(StandardTree::getStandardMethodId, methodIds)
                    .isNull(StandardTree::getSampleType)
                    .isNull(StandardTree::getModel));
            Map<StandardTree, Set<Integer>> matches = candidates.stream()
                    .filter(tree -> StringUtils.isNotBlank(tree.getSample())
                            && Objects.equals(tree.getLaboratory(), insOrder.getLaboratory()))
                    .collect(Collectors.toMap(tree -> tree,
                            tree -> standardTreeService.selectTemplateBindings(tree.getId()).stream()
                                    .map(StandardTreeTemplateBindingVo::getTemplateId)
                                    .collect(Collectors.toSet())));
            matches.entrySet().removeIf(entry -> !entry.getValue().containsAll(orderTemplateIds));
            if (matches.size() == 1) {
                map.put("standardTreeId", matches.keySet().iterator().next().getId());
            } else if (matches.size() > 1) {
                List<StandardTree> exactMatches = matches.entrySet().stream()
                        .filter(entry -> entry.getValue().equals(orderTemplateIds))
                        .map(Map.Entry::getKey).collect(Collectors.toList());
                if (exactMatches.size() == 1) {
                    map.put("standardTreeId", exactMatches.get(0).getId());
                }
            }
        }
        return map;
    }
@@ -850,7 +1071,6 @@
                                .eq(InsOrderStandardTemplate::getInsOrderId, id)
                                .select(InsOrderStandardTemplate::getTemplateId,
                                        InsOrderStandardTemplate::getTemperature,
                                        InsOrderStandardTemplate::getHumidity,
                                        InsOrderStandardTemplate::getDetectionTime,
                                        InsOrderStandardTemplate::getDetectionPlace,
                                        InsOrderStandardTemplate::getInsResult)
@@ -860,7 +1080,6 @@
                    TemplateConditionVo condition = new TemplateConditionVo();
                    condition.setTemplateId(snapshot.getTemplateId());
                    condition.setTemperature(snapshot.getTemperature());
                    condition.setHumidity(snapshot.getHumidity());
                    condition.setDetectionTime(snapshot.getDetectionTime());
                    condition.setDetectionPlace(snapshot.getDetectionPlace());
                    condition.setInsResult(snapshot.getInsResult());