cnas-process/src/main/java/com/ruoyi/process/service/impl/InspectionOrderServiceImpl.java
@@ -3,6 +3,7 @@
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -20,6 +21,7 @@
import com.ruoyi.inspect.service.InsOrderService;
import com.ruoyi.inspect.service.InsReportService;
import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
import com.ruoyi.process.dto.InspectionOrderDetailExportDto;
import com.ruoyi.process.dto.InspectionOrderDto;
import com.ruoyi.process.dto.InspectionOrderExportDto;
import com.ruoyi.process.mapper.InspectionOrderMapper;
@@ -90,14 +92,86 @@
    /**
     * 检验委托单分页查询
     * @param page
     * @param InspectionOrder
     * @return
     * 检验委托单分页查询:同一批次的样品合并成一行展示。
     * 过滤条件先匹配订单,再按批次补全成员,最后在内存里分组和分页。
     */
    @Override
    public IPage<InspectionOrderDto> pageInspectionOrder(Page page, InspectionOrder InspectionOrder) {
        return baseMapper.pageInspectionOrder(page, QueryWrappers.queryWrappers(InspectionOrder));
    public IPage<InspectionOrderDto> pageInspectionOrder(Page page, InspectionOrder inspectionOrderQuery) {
        QueryWrapper<InspectionOrder> queryWrapper = QueryWrappers.queryWrappers(inspectionOrderQuery);
        queryWrapper.orderByDesc("create_time").orderByDesc("inspection_order_id");
        List<InspectionOrder> matched = baseMapper.selectList(queryWrapper);
        Page<InspectionOrderDto> result = new Page<>(page.getCurrent(), page.getSize(), 0);
        if (matched.isEmpty()) {
            result.setRecords(Collections.emptyList());
            return result;
        }
        Set<String> batchNos = matched.stream()
                .map(InspectionOrder::getBatchNo)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toSet());
        List<InspectionOrder> allOrders = new ArrayList<>(matched);
        if (!batchNos.isEmpty()) {
            Set<Integer> matchedIds = matched.stream()
                    .map(InspectionOrder::getInspectionOrderId)
                    .collect(Collectors.toSet());
            allOrders.addAll(baseMapper.selectList(Wrappers.<InspectionOrder>lambdaQuery()
                    .in(InspectionOrder::getBatchNo, batchNos)
                    .notIn(InspectionOrder::getInspectionOrderId, matchedIds)));
        }
        Map<String, List<InspectionOrder>> membersByGroup = allOrders.stream()
                .collect(Collectors.groupingBy(this::batchGroupKey));
        Map<Integer, String> groupKeyById = allOrders.stream()
                .collect(Collectors.toMap(InspectionOrder::getInspectionOrderId, this::batchGroupKey, (first, ignored) -> first));
        Map<String, Integer> detailCountByGroup = new HashMap<>();
        List<InspectionOrderDetail> details = inspectionOrderDetailService.list(
                Wrappers.<InspectionOrderDetail>lambdaQuery()
                        .in(InspectionOrderDetail::getInspectionOrderId, groupKeyById.keySet())
                        .select(InspectionOrderDetail::getInspectionOrderId));
        for (InspectionOrderDetail detail : details) {
            String groupKey = groupKeyById.get(detail.getInspectionOrderId());
            if (groupKey != null) {
                detailCountByGroup.merge(groupKey, 1, Integer::sum);
            }
        }
        // 按匹配订单的先后顺序输出分组;代表单固定取组内最早的订单,保证翻页稳定
        LinkedHashMap<String, InspectionOrder> orderedGroups = new LinkedHashMap<>();
        for (InspectionOrder order : matched) {
            orderedGroups.putIfAbsent(batchGroupKey(order), order);
        }
        List<InspectionOrderDto> groups = new ArrayList<>();
        for (Map.Entry<String, InspectionOrder> entry : orderedGroups.entrySet()) {
            List<InspectionOrder> members = membersByGroup.getOrDefault(entry.getKey(),
                    Collections.singletonList(entry.getValue()));
            InspectionOrder representative = members.stream()
                    .min(Comparator.comparing(InspectionOrder::getInspectionOrderId))
                    .orElse(entry.getValue());
            InspectionOrderDto dto = new InspectionOrderDto();
            BeanUtils.copyProperties(representative, dto);
            dto.setMemberCount(members.size());
            dto.setDetailCount(detailCountByGroup.getOrDefault(entry.getKey(), 0));
            groups.add(dto);
        }
        long total = groups.size();
        int from = (int) Math.min(total, Math.max(0L, (page.getCurrent() - 1) * page.getSize()));
        int to = (int) Math.min(total, from + page.getSize());
        result.setTotal(total);
        result.setRecords(from >= to ? Collections.emptyList() : new ArrayList<>(groups.subList(from, to)));
        return result;
    }
    private String batchGroupKey(InspectionOrder order) {
        return StrUtil.isBlank(order.getBatchNo()) ? "#" + order.getInspectionOrderId() : order.getBatchNo();
    }
    private List<InspectionOrder> listBatchMembers(String batchNo) {
        return baseMapper.selectList(Wrappers.<InspectionOrder>lambdaQuery()
                .eq(InspectionOrder::getBatchNo, batchNo)
                .orderByAsc(InspectionOrder::getInspectionOrderId));
    }
    /**
@@ -220,6 +294,77 @@
                .collect(Collectors.toSet());
    }
    private void syncHeaderSampleFields(InspectionOrderDto inspectionOrder) {
        if (CollectionUtils.isEmpty(inspectionOrder.getOrderDetailList())) {
            return;
        }
        InspectionOrderDetail first = inspectionOrder.getOrderDetailList().get(0);
        if (StrUtil.isNotBlank(first.getSampleName())) {
            inspectionOrder.setSampleName(first.getSampleName());
        }
        if (StrUtil.isNotBlank(first.getQuantity())) {
            inspectionOrder.setQuantity(first.getQuantity());
        }
        if (StrUtil.isNotBlank(first.getSampleStatus())) {
            inspectionOrder.setSampleStatus(first.getSampleStatus());
        }
    }
    private void applyRequirementFields(InspectionOrderDto inspectionOrder) {
        if (inspectionOrder.getSampleDisposal() != null) {
            inspectionOrder.setProcessing(Integer.valueOf(2).equals(inspectionOrder.getSampleDisposal()) ? 1 : 0);
        }
        if (inspectionOrder.getReportDelivery() != null) {
            inspectionOrder.setSend(Integer.valueOf(1).equals(inspectionOrder.getReportDelivery()) ? 1 : 0);
        }
    }
    private void syncInsOrderRequirement(InspectionOrderDto inspectionOrder) {
        if (inspectionOrder.getInsOrderId() == null) {
            return;
        }
        InsOrder insOrder = new InsOrder();
        insOrder.setId(inspectionOrder.getInsOrderId());
        insOrder.setSubcontractType(inspectionOrder.getSubcontractType());
        if (inspectionOrder.getSubcontractType() != null) {
            insOrder.setSubcontractCompany(Integer.valueOf(1).equals(inspectionOrder.getSubcontractType())
                    ? (inspectionOrder.getSubcontractCompany() == null ? "" : inspectionOrder.getSubcontractCompany())
                    : "");
        }
        insOrder.setSampleDisposal(inspectionOrder.getSampleDisposal());
        insOrder.setResultEvaluation(inspectionOrder.getResultEvaluation());
        insOrder.setReportDelivery(inspectionOrder.getReportDelivery());
        insOrder.setReportAddress(inspectionOrder.getReportAddress());
        insOrder.setReportReceiver(inspectionOrder.getReportReceiver());
        if (inspectionOrder.getSampleDisposal() != null) {
            insOrder.setProcessing(inspectionOrder.getProcessing());
        }
        if (inspectionOrder.getReportDelivery() != null) {
            insOrder.setSend(inspectionOrder.getSend());
        }
        insOrderService.updateById(insOrder);
    }
    private void fillRequirementFields(InspectionOrder inspectionOrder, InspectionOrderDto dto, InsOrder insOrder) {
        Integer sampleDisposal = insOrder == null ? null : insOrder.getSampleDisposal();
        if (sampleDisposal == null) {
            sampleDisposal = inspectionOrder.getProcessing() != null && inspectionOrder.getProcessing() == 1 ? 2 : 1;
        }
        Integer reportDelivery = insOrder == null ? null : insOrder.getReportDelivery();
        if (reportDelivery == null) {
            reportDelivery = inspectionOrder.getSend() != null && inspectionOrder.getSend() == 1 ? 1 : 2;
        }
        Integer subcontractType = insOrder != null && insOrder.getSubcontractType() != null ? insOrder.getSubcontractType() : 0;
        dto.setSubcontractType(subcontractType);
        dto.setSubcontractCompany(subcontractType == 1 && insOrder != null && insOrder.getSubcontractCompany() != null
                ? insOrder.getSubcontractCompany() : "");
        dto.setSampleDisposal(sampleDisposal);
        dto.setResultEvaluation(insOrder != null && insOrder.getResultEvaluation() != null ? insOrder.getResultEvaluation() : 0);
        dto.setReportDelivery(reportDelivery);
        dto.setReportAddress(insOrder != null && insOrder.getReportAddress() != null ? insOrder.getReportAddress() : "");
        dto.setReportReceiver(insOrder != null && insOrder.getReportReceiver() != null ? insOrder.getReportReceiver() : "");
    }
    /**
     * 检验委托单修改
     * @param InspectionOrder
@@ -227,32 +372,143 @@
     */
    @Override
    public boolean updateInspectionOrder(InspectionOrderDto InspectionOrder) {
        if (StrUtil.isNotBlank(InspectionOrder.getBatchNo())) {
            return updateBatchInspectionOrder(InspectionOrder);
        }
        applyRequirementFields(InspectionOrder);
        syncHeaderSampleFields(InspectionOrder);
        baseMapper.updateById(InspectionOrder);
        // 删除之前的详情
        inspectionOrderDetailService.remove(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, InspectionOrder.getInspectionOrderId()));
        // 明细为 null 表示本次不维护明细,保留原数据;空数组表示清空
        if (InspectionOrder.getOrderDetailList() != null) {
            inspectionOrderDetailService.remove(Wrappers.<InspectionOrderDetail>lambdaQuery()
                    .eq(InspectionOrderDetail::getInspectionOrderId, InspectionOrder.getInspectionOrderId()));
        // 新增详情
        for (InspectionOrderDetail InspectionOrderDetail : InspectionOrder.getOrderDetailList()) {
            InspectionOrderDetail.setInspectionOrderId(InspectionOrder.getInspectionOrderId());
            if (CollectionUtils.isNotEmpty(InspectionOrder.getOrderDetailList())) {
                for (InspectionOrderDetail InspectionOrderDetail : InspectionOrder.getOrderDetailList()) {
                    InspectionOrderDetail.setInspectionOrderId(InspectionOrder.getInspectionOrderId());
                }
                inspectionOrderDetailService.saveBatch(InspectionOrder.getOrderDetailList());
            }
        }
        inspectionOrderDetailService.saveBatch(InspectionOrder.getOrderDetailList());
        //修改7.9报告结果
        syncInsOrderRequirement(InspectionOrder);
        syncProcessReport(InspectionOrder.getInsOrderId());
        return true;
    }
    /**
     * 合并编辑:公共委托信息广播到整批,明细按行所属委托单写回。
     */
    private boolean updateBatchInspectionOrder(InspectionOrderDto inspectionOrder) {
        List<InspectionOrder> members = listBatchMembers(inspectionOrder.getBatchNo());
        if (members.isEmpty()) {
            throw new ErrorException("批次数据不存在");
        }
        Set<Integer> memberIds = members.stream()
                .map(InspectionOrder::getInspectionOrderId)
                .collect(Collectors.toSet());
        Integer representativeId = inspectionOrder.getInspectionOrderId();
        if (representativeId == null || !memberIds.contains(representativeId)) {
            representativeId = members.get(0).getInspectionOrderId();
        }
        applyRequirementFields(inspectionOrder);
        for (InspectionOrder member : members) {
            copyBatchHeader(inspectionOrder, member);
            baseMapper.updateById(member);
        }
        if (inspectionOrder.getOrderDetailList() != null) {
            inspectionOrderDetailService.remove(Wrappers.<InspectionOrderDetail>lambdaQuery()
                    .in(InspectionOrderDetail::getInspectionOrderId, memberIds));
            if (CollectionUtils.isNotEmpty(inspectionOrder.getOrderDetailList())) {
                Map<Integer, List<InspectionOrderDetail>> detailsByOwner = new LinkedHashMap<>();
                for (InspectionOrderDetail detail : inspectionOrder.getOrderDetailList()) {
                    Integer ownerId = detail.getInspectionOrderId();
                    if (ownerId == null || !memberIds.contains(ownerId)) {
                        ownerId = representativeId;
                    }
                    detail.setInspectionOrderDetailId(null);
                    detail.setInspectionOrderId(ownerId);
                    detailsByOwner.computeIfAbsent(ownerId, key -> new ArrayList<>()).add(detail);
                }
                final Integer fallbackOwnerId = representativeId;
                for (Map.Entry<Integer, List<InspectionOrderDetail>> entry : detailsByOwner.entrySet()) {
                    InspectionOrder member = members.stream()
                            .filter(item -> item.getInspectionOrderId().equals(entry.getKey()))
                            .findFirst()
                            .orElse(members.stream()
                                    .filter(item -> item.getInspectionOrderId().equals(fallbackOwnerId))
                                    .findFirst().orElse(null));
                    InspectionOrderDetail first = entry.getValue().get(0);
                    if (member != null) {
                        if (StrUtil.isNotBlank(first.getSampleName())) {
                            member.setSampleName(first.getSampleName());
                        }
                        if (StrUtil.isNotBlank(first.getQuantity())) {
                            member.setQuantity(first.getQuantity());
                        }
                        if (StrUtil.isNotBlank(first.getSampleStatus())) {
                            member.setSampleStatus(first.getSampleStatus());
                        }
                        baseMapper.updateById(member);
                    }
                    inspectionOrderDetailService.saveBatch(entry.getValue());
                }
            }
        }
        for (InspectionOrder member : members) {
            InspectionOrderDto memberDto = new InspectionOrderDto();
            BeanUtils.copyProperties(inspectionOrder, memberDto);
            memberDto.setInspectionOrderId(member.getInspectionOrderId());
            memberDto.setInsOrderId(member.getInsOrderId());
            memberDto.setEntrustCode(member.getEntrustCode());
            memberDto.setBatchNo(member.getBatchNo());
            syncInsOrderRequirement(memberDto);
            syncProcessReport(member.getInsOrderId());
        }
        return true;
    }
    private void copyBatchHeader(InspectionOrderDto source, InspectionOrder target) {
        target.setCommissionUnit(source.getCommissionUnit());
        target.setCommissionDate(source.getCommissionDate());
        target.setCommissionUser(source.getCommissionUser());
        target.setCommissionPhone(source.getCommissionPhone());
        target.setProduction(source.getProduction());
        target.setIsLeave(source.getIsLeave());
        target.setSampleSource(source.getSampleSource());
        target.setProcessing(source.getProcessing());
        target.setSend(source.getSend());
        target.setCriterionRule(source.getCriterionRule());
        target.setCriterionRuleRemark(source.getCriterionRuleRemark());
        target.setGeneralOfficeUser(source.getGeneralOfficeUser());
        target.setReceiptData(source.getReceiptData());
        target.setReceiveStatus(source.getReceiveStatus());
        target.setFeeStatus(source.getFeeStatus());
        target.setSampleTakerUser(source.getSampleTakerUser());
        target.setSampleData(source.getSampleData());
        target.setAppointed(source.getAppointed());
    }
    private void syncProcessReport(Integer insOrderId) {
        if (insOrderId == null) {
            return;
        }
        ProcessReport processReport = processReportService.getOne(Wrappers.<ProcessReport>lambdaQuery()
                .eq(ProcessReport::getInspectionOrderId, InspectionOrder.getInsOrderId())
                .eq(ProcessReport::getInspectionOrderId, insOrderId)
                .last("limit 1"));
        InsReport insReport = insReportService.getOne(Wrappers.<InsReport>lambdaQuery()
                .eq(InsReport::getInsOrderId, InspectionOrder.getInsOrderId())
                .eq(InsReport::getInsOrderId, insOrderId)
                .orderByDesc(InsReport::getId)
                .last("limit 1"));
        if (processReport != null && insReport != null) {
            processReport.setInsReportCode(insReport.getCode());//报告编号使用正式报告编号
            processReportService.updateById(processReport);
        }
        return true;
    }
    /**
@@ -262,12 +518,15 @@
     */
    @Override
    public boolean delInspectionOrder(Integer inspectionOrderId) {
        InspectionOrder order = baseMapper.selectById(inspectionOrderId);
        inspectionOrderDetailService.remove(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId));
        baseMapper.deleteById(inspectionOrderId);
        //删除7.8报告
        processReportService.remove(Wrappers.<ProcessReport>lambdaQuery()
                .eq(ProcessReport::getInspectionOrderId, inspectionOrderId));
        //删除7.8报告(该表 inspection_order_id 存的是成品订单id)
        if (order != null && order.getInsOrderId() != null) {
            processReportService.remove(Wrappers.<ProcessReport>lambdaQuery()
                    .eq(ProcessReport::getInspectionOrderId, order.getInsOrderId()));
        }
        //删除对应的设备使用记录
        processOrderDeviceService.remove(Wrappers.<ProcessOrderDevice>lambdaQuery()
                .eq(ProcessOrderDevice::getInspectionOrderId, inspectionOrderId));
@@ -285,6 +544,21 @@
        return true;
    }
    @Override
    public boolean delInspectionOrder(Integer inspectionOrderId, String batchNo) {
        if (StrUtil.isBlank(batchNo)) {
            return delInspectionOrder(inspectionOrderId);
        }
        List<InspectionOrder> members = listBatchMembers(batchNo);
        if (members.isEmpty()) {
            return delInspectionOrder(inspectionOrderId);
        }
        for (InspectionOrder member : members) {
            delInspectionOrder(member.getInspectionOrderId());
        }
        return true;
    }
    /**
     * 检验委托单查看详情
     * @param inspectionOrderId
@@ -296,9 +570,81 @@
        InspectionOrderDto inspectionOrderDto = new InspectionOrderDto();
        BeanUtils.copyProperties(inspectionOrder, inspectionOrderDto);
        InsOrder insOrder = inspectionOrder.getInsOrderId() == null ? null : insOrderService.getById(inspectionOrder.getInsOrderId());
        fillRequirementFields(inspectionOrder, inspectionOrderDto, insOrder);
        if (inspectionOrderDto.getSampleSource() == null) {
            inspectionOrderDto.setSampleSource(1);
        }
        if (inspectionOrderDto.getReceiveStatus() == null) {
            inspectionOrderDto.setReceiveStatus(1);
        }
        if (inspectionOrderDto.getFeeStatus() == null) {
            inspectionOrderDto.setFeeStatus(2);
        }
        // 查询详细信息
        inspectionOrderDto.setOrderDetailList(inspectionOrderDetailService.list(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId)));
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId)
                .orderByAsc(InspectionOrderDetail::getInspectionOrderDetailId)));
        return inspectionOrderDto;
    }
    /**
     * 按批次查看:头信息取代表单,明细合并整批样品。
     */
    @Override
    public InspectionOrderDto getInspectionOrderOne(Integer inspectionOrderId, String batchNo) {
        if (StrUtil.isBlank(batchNo)) {
            return getInspectionOrderOne(inspectionOrderId);
        }
        List<InspectionOrder> members = listBatchMembers(batchNo);
        if (members.isEmpty()) {
            return getInspectionOrderOne(inspectionOrderId);
        }
        InspectionOrder representative = members.stream()
                .filter(item -> item.getInspectionOrderId().equals(inspectionOrderId))
                .findFirst()
                .orElse(members.get(0));
        InspectionOrderDto inspectionOrderDto = new InspectionOrderDto();
        BeanUtils.copyProperties(representative, inspectionOrderDto);
        InsOrder insOrder = representative.getInsOrderId() == null ? null
                : insOrderService.getById(representative.getInsOrderId());
        fillRequirementFields(representative, inspectionOrderDto, insOrder);
        if (inspectionOrderDto.getSampleSource() == null) {
            inspectionOrderDto.setSampleSource(1);
        }
        if (inspectionOrderDto.getReceiveStatus() == null) {
            inspectionOrderDto.setReceiveStatus(1);
        }
        if (inspectionOrderDto.getFeeStatus() == null) {
            inspectionOrderDto.setFeeStatus(2);
        }
        inspectionOrderDto.setMemberCount(members.size());
        Map<Integer, String> entrustCodeById = members.stream()
                .collect(Collectors.toMap(InspectionOrder::getInspectionOrderId,
                        item -> item.getEntrustCode() == null ? "" : item.getEntrustCode()));
        List<InspectionOrderDetail> details = inspectionOrderDetailService.list(
                Wrappers.<InspectionOrderDetail>lambdaQuery()
                        .in(InspectionOrderDetail::getInspectionOrderId, entrustCodeById.keySet())
                        .orderByAsc(InspectionOrderDetail::getInspectionOrderId)
                        .orderByAsc(InspectionOrderDetail::getInspectionOrderDetailId));
        Map<Integer, List<InspectionOrderDetail>> detailsByOwner = details.stream()
                .collect(Collectors.groupingBy(InspectionOrderDetail::getInspectionOrderId,
                        LinkedHashMap::new, Collectors.toList()));
        for (InspectionOrder member : members) {
            List<InspectionOrderDetail> ownerDetails = detailsByOwner.get(member.getInspectionOrderId());
            if (CollectionUtils.isEmpty(ownerDetails)) {
                continue;
            }
            List<String> codes = buildDetailEntrustCodes(member.getEntrustCode(), ownerDetails);
            for (int index = 0; index < ownerDetails.size(); index++) {
                ownerDetails.get(index).setEntrustCode(codes.get(index));
            }
        }
        inspectionOrderDto.setOrderDetailList(details);
        inspectionOrderDto.setDetailCount(details.size());
        return inspectionOrderDto;
    }
@@ -323,6 +669,27 @@
        inspectionOrderDto.setAppointed(order.getAppointed());//约定时间
        inspectionOrderDto.setSend(order.getSend());//报告发送方式
        inspectionOrderDto.setCommissionPhone(order.getPhone());//报告发送方式
        Integer subcontractType = order.getSubcontractType() == null ? 0 : order.getSubcontractType();
        inspectionOrderDto.setSubcontractType(subcontractType);
        inspectionOrderDto.setSubcontractCompany(subcontractType == 1 && order.getSubcontractCompany() != null
                ? order.getSubcontractCompany() : "");
        Integer sampleDisposal = order.getSampleDisposal();
        if (sampleDisposal == null) {
            sampleDisposal = order.getProcessing() != null && order.getProcessing() == 1 ? 2 : 1;
        }
        inspectionOrderDto.setSampleDisposal(sampleDisposal);
        inspectionOrderDto.setResultEvaluation(order.getResultEvaluation() == null ? 0 : order.getResultEvaluation());
        Integer reportDelivery = order.getReportDelivery();
        if (reportDelivery == null) {
            reportDelivery = order.getSend() != null && order.getSend() == 1 ? 1 : 2;
        }
        inspectionOrderDto.setReportDelivery(reportDelivery);
        inspectionOrderDto.setReportAddress(order.getReportAddress() == null ? "" : order.getReportAddress());
        inspectionOrderDto.setReportReceiver(order.getReportReceiver() == null ? "" : order.getReportReceiver());
        inspectionOrderDto.setSampleSource(1);
        inspectionOrderDto.setReceiveStatus(1);
        inspectionOrderDto.setFeeStatus(2);
        return inspectionOrderDto;
    }
@@ -371,16 +738,99 @@
    @Override
    public void exportInspectionOrder(Integer inspectionOrderId, HttpServletResponse response) {
        InspectionOrder inspectionOrder = baseMapper.selectById(inspectionOrderId);
        InspectionOrderExportDto inspectionOrderExportDto = extracted(inspectionOrder);
        List<InspectionOrderDetail> list = inspectionOrderDetailService.list(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId));
        int index = 1;
        for (InspectionOrderDetail inspectionOrderDetail : list) {
            inspectionOrderDetail.setIndex(index);
            index++;
        }
        exportSingleInspectionOrder(inspectionOrderId, response);
    }
    @Override
    public void exportInspectionOrder(Integer inspectionOrderId, String batchNo, HttpServletResponse response) {
        if (StrUtil.isBlank(batchNo)) {
            exportSingleInspectionOrder(inspectionOrderId, response);
            return;
        }
        List<InspectionOrder> members = listBatchMembers(batchNo);
        if (members.isEmpty()) {
            exportSingleInspectionOrder(inspectionOrderId, response);
            return;
        }
        InspectionOrder representative = members.stream()
                .filter(item -> item.getInspectionOrderId().equals(inspectionOrderId))
                .findFirst()
                .orElse(members.get(0));
        InsOrder insOrder = representative.getInsOrderId() == null ? null
                : insOrderService.getById(representative.getInsOrderId());
        InspectionOrderExportDto inspectionOrderExportDto = extracted(representative, insOrder);
        Map<Integer, List<InspectionOrderDetail>> detailsByOwner = inspectionOrderDetailService.list(
                        Wrappers.<InspectionOrderDetail>lambdaQuery()
                                .in(InspectionOrderDetail::getInspectionOrderId, members.stream()
                                        .map(InspectionOrder::getInspectionOrderId)
                                        .collect(Collectors.toList()))
                                .orderByAsc(InspectionOrderDetail::getInspectionOrderId)
                                .orderByAsc(InspectionOrderDetail::getInspectionOrderDetailId))
                .stream()
                .collect(Collectors.groupingBy(InspectionOrderDetail::getInspectionOrderId,
                        LinkedHashMap::new, Collectors.toList()));
        List<InspectionOrderDetailExportDto> detailList = new ArrayList<>();
        for (InspectionOrder member : members) {
            List<InspectionOrderDetail> ownerDetails = detailsByOwner.get(member.getInspectionOrderId());
            if (CollectionUtils.isEmpty(ownerDetails)) {
                continue;
            }
            List<String> detailEntrustCodes = buildDetailEntrustCodes(member.getEntrustCode(), ownerDetails);
            for (int i = 0; i < ownerDetails.size(); i++) {
                InspectionOrderDetail inspectionOrderDetail = ownerDetails.get(i);
                InspectionOrderDetailExportDto row = new InspectionOrderDetailExportDto();
                row.setSampleName(StrUtil.isNotBlank(inspectionOrderDetail.getSampleName())
                        ? inspectionOrderDetail.getSampleName() : member.getSampleName());
                row.setSampleNumber(inspectionOrderDetail.getSampleNumber());
                row.setTestItem(inspectionOrderDetail.getTestItem());
                row.setQuantity(StrUtil.isNotBlank(inspectionOrderDetail.getQuantity())
                        ? inspectionOrderDetail.getQuantity() : member.getQuantity());
                row.setSampleStatus(StrUtil.isNotBlank(inspectionOrderDetail.getSampleStatus())
                        ? inspectionOrderDetail.getSampleStatus() : member.getSampleStatus());
                row.setTestStandard(inspectionOrderDetail.getTestStandard());
                row.setRemark(inspectionOrderDetail.getRemark());
                row.setEntrustCode(detailEntrustCodes.get(i));
                detailList.add(row);
            }
        }
        inspectionOrderExportDto.setProjectName(detailList.size() + "个样品");
        writeInspectionOrderExport(inspectionOrderExportDto, detailList, response);
    }
    private void exportSingleInspectionOrder(Integer inspectionOrderId, HttpServletResponse response) {
        InspectionOrder inspectionOrder = baseMapper.selectById(inspectionOrderId);
        InsOrder insOrder = inspectionOrder.getInsOrderId() == null ? null : insOrderService.getById(inspectionOrder.getInsOrderId());
        InspectionOrderExportDto inspectionOrderExportDto = extracted(inspectionOrder, insOrder);
        List<InspectionOrderDetail> list = inspectionOrderDetailService.list(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId)
                .orderByAsc(InspectionOrderDetail::getInspectionOrderDetailId));
        List<String> detailEntrustCodes = buildDetailEntrustCodes(inspectionOrder.getEntrustCode(), list);
        List<InspectionOrderDetailExportDto> detailList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            InspectionOrderDetail inspectionOrderDetail = list.get(i);
            InspectionOrderDetailExportDto row = new InspectionOrderDetailExportDto();
            row.setSampleName(StrUtil.isNotBlank(inspectionOrderDetail.getSampleName())
                    ? inspectionOrderDetail.getSampleName() : inspectionOrder.getSampleName());
            row.setSampleNumber(inspectionOrderDetail.getSampleNumber());
            row.setTestItem(inspectionOrderDetail.getTestItem());
            row.setQuantity(StrUtil.isNotBlank(inspectionOrderDetail.getQuantity())
                    ? inspectionOrderDetail.getQuantity() : inspectionOrder.getQuantity());
            row.setSampleStatus(StrUtil.isNotBlank(inspectionOrderDetail.getSampleStatus())
                    ? inspectionOrderDetail.getSampleStatus() : inspectionOrder.getSampleStatus());
            row.setTestStandard(inspectionOrderDetail.getTestStandard());
            row.setRemark(inspectionOrderDetail.getRemark());
            row.setEntrustCode(detailEntrustCodes.get(i));
            detailList.add(row);
        }
        inspectionOrderExportDto.setProjectName(detailList.size() + "个样品");
        writeInspectionOrderExport(inspectionOrderExportDto, detailList, response);
    }
    private void writeInspectionOrderExport(InspectionOrderExportDto inspectionOrderExportDto,
                                            List<InspectionOrderDetailExportDto> detailList,
                                            HttpServletResponse response) {
        // 获取路径
        InputStream inputStream = this.getClass().getResourceAsStream("/static/inspection-order.docx");
        Configure configure = Configure.builder()
@@ -389,13 +839,14 @@
        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
                new HashMap<String, Object>() {{
                    put("inspectionOrder", inspectionOrderExportDto);
                    put("inspectionOrderDetailList", list);
                    put("inspectionOrderDetailList", detailList);
                }});
        try {
            response.setContentType("application/msword");
            String fileName = URLEncoder.encode(
                    inspectionOrderExportDto.getSampleName(), "UTF-8");
                    StrUtil.isBlank(inspectionOrderExportDto.getSampleName())
                            ? "客户检测委托书" : inspectionOrderExportDto.getSampleName(), "UTF-8");
            response.setHeader("Content-disposition",
                    "attachment;filename=" + fileName + ".docx");
            OutputStream os = response.getOutputStream();
@@ -409,7 +860,7 @@
        }
    }
    private InspectionOrderExportDto extracted(InspectionOrder inspectionOrder) {
    private InspectionOrderExportDto extracted(InspectionOrder inspectionOrder, InsOrder insOrder) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
        InspectionOrderExportDto inspectionOrderExportDto = new InspectionOrderExportDto();
        BeanUtils.copyProperties(inspectionOrder, inspectionOrderExportDto);
@@ -428,13 +879,99 @@
        }
        inspectionOrderExportDto.setIsLeave1(inspectionOrder.getIsLeave() != null && inspectionOrder.getIsLeave() == 1? "☑" : "□");
        inspectionOrderExportDto.setIsLeave2(inspectionOrder.getIsLeave() != null && inspectionOrder.getIsLeave() == 0? "☑" : "□");
        inspectionOrderExportDto.setSend0(inspectionOrder.getSend() != null && inspectionOrder.getSend() == 1? "☑" : "□");
        inspectionOrderExportDto.setSend1(inspectionOrder.getSend() != null && inspectionOrder.getSend() == 0? "☑" : "□");
        inspectionOrderExportDto.setIsLeave2(inspectionOrder.getIsLeave() != null && inspectionOrder.getIsLeave() != 1? "☑" : "□");
        inspectionOrderExportDto.setProcessing0(inspectionOrder.getProcessing() != null && inspectionOrder.getProcessing() == 0? "☑" : "□");
        inspectionOrderExportDto.setProcessing1(inspectionOrder.getProcessing() != null && inspectionOrder.getProcessing() == 1? "☑" : "□");
        inspectionOrderExportDto.setCriterionRule0(inspectionOrder.getCriterionRule() != null && inspectionOrder.getCriterionRule() == 0? "☑" : "□");
        inspectionOrderExportDto.setCriterionRule1(inspectionOrder.getCriterionRule() != null && inspectionOrder.getCriterionRule() == 1? "☑" : "□");
        Integer subcontractType = insOrder != null && insOrder.getSubcontractType() != null ? insOrder.getSubcontractType() : 0;
        Integer sampleDisposal = insOrder == null ? null : insOrder.getSampleDisposal();
        if (sampleDisposal == null) {
            sampleDisposal = inspectionOrder.getProcessing() != null && inspectionOrder.getProcessing() == 1 ? 2 : 1;
        }
        Integer resultEvaluation = insOrder != null && insOrder.getResultEvaluation() != null ? insOrder.getResultEvaluation() : 0;
        Integer reportDelivery = insOrder == null ? null : insOrder.getReportDelivery();
        if (reportDelivery == null) {
            reportDelivery = inspectionOrder.getSend() != null && inspectionOrder.getSend() == 1 ? 1 : 2;
        }
        inspectionOrderExportDto.setSubcontractType(subcontractType);
        inspectionOrderExportDto.setSubcontractCompany(subcontractType == 1 && insOrder != null && insOrder.getSubcontractCompany() != null
                ? insOrder.getSubcontractCompany() : "");
        inspectionOrderExportDto.setSubcontractYes(subcontractType == 1 ? "☑" : "□");
        inspectionOrderExportDto.setSubcontractNo(subcontractType == 0 ? "☑" : "□");
        inspectionOrderExportDto.setSampleDisposal(sampleDisposal);
        inspectionOrderExportDto.setResultEvaluation(resultEvaluation);
        inspectionOrderExportDto.setResultEvaluationYes(resultEvaluation == 1 ? "☑" : "□");
        inspectionOrderExportDto.setResultEvaluationNo(resultEvaluation == 0 ? "☑" : "□");
        inspectionOrderExportDto.setReportDelivery(reportDelivery);
        inspectionOrderExportDto.setSendSelf(reportDelivery == 1 ? "☑" : "□");
        inspectionOrderExportDto.setSendExpress(reportDelivery == 2 ? "☑" : "□");
        inspectionOrderExportDto.setSendElectronic(reportDelivery == 3 ? "☑" : "□");
        boolean express = reportDelivery == 2;
        inspectionOrderExportDto.setReportAddress(express && insOrder != null && insOrder.getReportAddress() != null
                ? insOrder.getReportAddress() : "");
        inspectionOrderExportDto.setReportReceiver(express && insOrder != null && insOrder.getReportReceiver() != null
                ? insOrder.getReportReceiver() : "");
        int sampleSource = inspectionOrder.getSampleSource() == null ? 1 : inspectionOrder.getSampleSource();
        int receiveStatus = inspectionOrder.getReceiveStatus() == null ? 1 : inspectionOrder.getReceiveStatus();
        int feeStatus = inspectionOrder.getFeeStatus() == null ? 2 : inspectionOrder.getFeeStatus();
        inspectionOrderExportDto.setSourceSend(sampleSource == 1 ? "☑" : "□");
        inspectionOrderExportDto.setSourceSample(sampleSource == 2 ? "☑" : "□");
        inspectionOrderExportDto.setReceiveOk(receiveStatus == 1 ? "☑" : "□");
        inspectionOrderExportDto.setReceiveDefect(receiveStatus == 2 ? "☑" : "□");
        inspectionOrderExportDto.setReceiveReject(receiveStatus == 3 ? "☑" : "□");
        inspectionOrderExportDto.setFeeReceived(feeStatus == 1 ? "☑" : "□");
        inspectionOrderExportDto.setFeeUnpaid(feeStatus == 2 ? "☑" : "□");
        inspectionOrderExportDto.setFeePrepaid(feeStatus == 3 ? "☑" : "□");
        return inspectionOrderExportDto;
    }
    private static String buildDetailEntrustCode(String orderEntrustCode, String sampleNumber) {
        if (StrUtil.isBlank(orderEntrustCode) || StrUtil.isBlank(sampleNumber)) {
            return orderEntrustCode;
        }
        String trimmed = sampleNumber.trim();
        String suffix = trimmed.length() >= 2 ? trimmed.substring(trimmed.length() - 2) : trimmed;
        int index = orderEntrustCode.lastIndexOf('-');
        return index < 0 ? orderEntrustCode : orderEntrustCode.substring(0, index + 1) + suffix;
    }
    /**
     * 逐行委托编号:默认取样品编号后两位;出现重复或为空时用行号兜底,保证不重复。
     */
    private static List<String> buildDetailEntrustCodes(String orderEntrustCode, List<InspectionOrderDetail> details) {
        List<String> bases = details.stream()
                .map(detail -> buildDetailEntrustCode(orderEntrustCode, detail.getSampleNumber()))
                .map(base -> base == null ? "" : base)
                .collect(Collectors.toList());
        Map<String, Integer> counts = new HashMap<>();
        for (String base : bases) {
            counts.merge(base, 1, Integer::sum);
        }
        String prefix = "";
        if (StrUtil.isNotBlank(orderEntrustCode)) {
            int index = orderEntrustCode.lastIndexOf('-');
            prefix = index < 0 ? orderEntrustCode + "-" : orderEntrustCode.substring(0, index + 1);
        }
        Set<String> used = new HashSet<>();
        List<String> result = new ArrayList<>();
        for (int i = 0; i < bases.size(); i++) {
            String base = bases.get(i);
            String candidate = base;
            if (StrUtil.isBlank(base) || counts.getOrDefault(base, 0) > 1 || used.contains(candidate)) {
                int seq = i + 1;
                candidate = prefix + String.format("%02d", seq);
                while (used.contains(candidate)) {
                    seq += bases.size();
                    candidate = prefix + String.format("%02d", seq);
                }
            }
            used.add(candidate);
            result.add(candidate);
        }
        return result;
    }
}