成品下单批量下单接口与检验委托单批次合并(batch_no),含数据库加列脚本
已添加4个文件
已修改15个文件
992 ■■■■■ 文件已修改
cnas-process/src/main/java/com/ruoyi/process/controller/InspectionOrderController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/dto/InspectionOrderDetailExportDto.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/dto/InspectionOrderDto.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/dto/InspectionOrderExportDto.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/listener/NewJuOrderInspectionListener.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/pojo/InspectionOrder.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/pojo/InspectionOrderDetail.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/service/InspectionOrderService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/service/impl/InspectionOrderServiceImpl.java 611 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/mapper/InspectionOrderDetailMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/mapper/InspectionOrderMapper.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/resources/static/inspection-order.docx 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/dto/InsBatchPlaceOrderDto.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/dto/InsBatchSampleDto.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/lims_delta.sql 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cnas-process/src/main/java/com/ruoyi/process/controller/InspectionOrderController.java
@@ -70,8 +70,8 @@
     */
    @Operation(summary = "委托单检验删除")
    @DeleteMapping("/delInspectionOrder")
    public Result delInspectionOrder(Integer inspectionOrderId){
        return Result.success(inspectionOrderService.delInspectionOrder(inspectionOrderId));
    public Result delInspectionOrder(Integer inspectionOrderId, String batchNo){
        return Result.success(inspectionOrderService.delInspectionOrder(inspectionOrderId, batchNo));
    }
    /**
@@ -80,8 +80,8 @@
     */
    @Operation(summary = "委托单检验查看详情")
    @GetMapping("/getInspectionOrderOne")
    public Result<InspectionOrderDto> getInspectionOrderOne(Integer inspectionOrderId){
        return Result.success(inspectionOrderService.getInspectionOrderOne(inspectionOrderId));
    public Result<InspectionOrderDto> getInspectionOrderOne(Integer inspectionOrderId, String batchNo){
        return Result.success(inspectionOrderService.getInspectionOrderOne(inspectionOrderId, batchNo));
    }
    /**
@@ -120,7 +120,7 @@
     */
    @Operation(summary = "导出检验委托单")
    @GetMapping("/exportInspectionOrder")
    public void exportInspectionOrder(Integer inspectionOrderId, HttpServletResponse response){
        inspectionOrderService.exportInspectionOrder(inspectionOrderId, response);
    public void exportInspectionOrder(Integer inspectionOrderId, String batchNo, HttpServletResponse response){
        inspectionOrderService.exportInspectionOrder(inspectionOrderId, batchNo, response);
    }
}
cnas-process/src/main/java/com/ruoyi/process/dto/InspectionOrderDetailExportDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.process.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
 * å®¢æˆ·æ£€æµ‹å§”托书样品信息行导出数据
 */
@Data
public class InspectionOrderDetailExportDto {
    @Schema(description = "样品名称")
    private String sampleName;
    @Schema(description = "样品编号")
    private String sampleNumber;
    @Schema(description = "检测项目")
    private String testItem;
    @Schema(description = "样品数量")
    private String quantity;
    @Schema(description = "样品状态")
    private String sampleStatus;
    @Schema(description = "检测依据")
    private String testStandard;
    @Schema(description = "备注信息")
    private String remark;
    @Schema(description = "委托编号")
    private String entrustCode;
}
cnas-process/src/main/java/com/ruoyi/process/dto/InspectionOrderDto.java
@@ -16,4 +16,31 @@
    @Schema(description = "委托检验单详情")
    private List<InspectionOrderDetail> orderDetailList;
    @Schema(description = "分包内容 0无 1有")
    private Integer subcontractType;
    @Schema(description = "分包单位信息")
    private String subcontractCompany;
    @Schema(description = "样品处置 1委托方取回 2检测方处置")
    private Integer sampleDisposal;
    @Schema(description = "对结果评价 0否 1是")
    private Integer resultEvaluation;
    @Schema(description = "报告交付方式 1自取 2快递 3电子传送")
    private Integer reportDelivery;
    @Schema(description = "报告寄送地址")
    private String reportAddress;
    @Schema(description = "收件人及电话")
    private String reportReceiver;
    @Schema(description = "样品明细行数")
    private Integer detailCount;
    @Schema(description = "同批次样品数")
    private Integer memberCount;
}
cnas-process/src/main/java/com/ruoyi/process/dto/InspectionOrderExportDto.java
@@ -17,17 +17,77 @@
    @Schema(description = "留样:0 å¦")
    private String isLeave2 = "□";
    @Schema(description = "报告发送方式 1:自取")
    private String send1 = "□";
    @Schema(description = "分包内容 0无 1有")
    private Integer subcontractType;
    @Schema(description = "报告发送方式 0:其他")
    private String send0 = "□";
    @Schema(description = "分包单位信息")
    private String subcontractCompany;
    @Schema(description = "分包内容 1:有")
    private String subcontractYes = "□";
    @Schema(description = "分包内容 0:无")
    private String subcontractNo = "□";
    @Schema(description = "样品处置 1委托方取回 2检测方处置")
    private Integer sampleDisposal;
    @Schema(description = "样品处理方式 1:实验室处理")
    private String processing1 = "□";
    @Schema(description = "样品处理方式 0:委托单位取回")
    private String processing0 = "□";
    @Schema(description = "对结果评价 0否 1是")
    private Integer resultEvaluation;
    @Schema(description = "对结果评价 1:是")
    private String resultEvaluationYes = "□";
    @Schema(description = "对结果评价 0:否")
    private String resultEvaluationNo = "□";
    @Schema(description = "检测来源 1:送样")
    private String sourceSend = "□";
    @Schema(description = "检测来源 2:采样")
    private String sourceSample = "□";
    @Schema(description = "接收样品状态 1:符合要求")
    private String receiveOk = "□";
    @Schema(description = "接收样品状态 2:有缺陷,但不影响检测")
    private String receiveDefect = "□";
    @Schema(description = "接收样品状态 3:不符合检测要求")
    private String receiveReject = "□";
    @Schema(description = "检测费 1:已收")
    private String feeReceived = "□";
    @Schema(description = "检测费 2:未收")
    private String feeUnpaid = "□";
    @Schema(description = "检测费 3:预收")
    private String feePrepaid = "□";
    @Schema(description = "报告交付方式 1自取 2快递 3电子传送")
    private Integer reportDelivery;
    @Schema(description = "报告交付方式 1:自取")
    private String sendSelf = "□";
    @Schema(description = "报告交付方式 2:快递")
    private String sendExpress = "□";
    @Schema(description = "报告交付方式 3:电子传送")
    private String sendElectronic = "□";
    @Schema(description = "报告寄送地址")
    private String reportAddress;
    @Schema(description = "收件人及电话")
    private String reportReceiver;
    @Schema(description = "判断规则 1:考虑不确定度")
    private String criterionRule1 = "□";
@@ -47,5 +107,8 @@
    @Schema(description = "约定时间 å­—符串格式:yyyyå¹´MM月dd")
    private String appointedString;
    @Schema(description = "项目名称(N个样品)")
    private String projectName;
}
cnas-process/src/main/java/com/ruoyi/process/listener/NewJuOrderInspectionListener.java
@@ -2,16 +2,24 @@
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.inspect.event.NewJuOrderPlacedEvent;
import com.ruoyi.inspect.mapper.InsOrderStandardTemplateMapper;
import com.ruoyi.inspect.mapper.InsSampleMapper;
import com.ruoyi.inspect.pojo.InsOrder;
import com.ruoyi.inspect.pojo.InsOrderStandardTemplate;
import com.ruoyi.inspect.pojo.InsSample;
import com.ruoyi.inspect.service.InsOrderService;
import com.ruoyi.process.mapper.InspectionOrderDetailMapper;
import com.ruoyi.process.mapper.InspectionOrderMapper;
import com.ruoyi.process.pojo.InspectionOrder;
import com.ruoyi.process.pojo.InspectionOrderDetail;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;
/**
 * æ–°èšæˆå“ä¸‹å•成功后,自动生成一条检验委托单。
@@ -22,6 +30,9 @@
    private final InsOrderService insOrderService;
    private final InspectionOrderMapper inspectionOrderMapper;
    private final InspectionOrderDetailMapper inspectionOrderDetailMapper;
    private final InsSampleMapper insSampleMapper;
    private final InsOrderStandardTemplateMapper insOrderStandardTemplateMapper;
    @EventListener
    public void onNewJuOrderPlaced(NewJuOrderPlacedEvent event) {
@@ -34,6 +45,7 @@
                .eq(InspectionOrder::getInsOrderId, order.getId()).last("limit 1"));
        if (existingOrder != null) {
            existingOrder.setEntrustCode(order.getEntrustCode());
            existingOrder.setBatchNo(order.getBatchNo());
            existingOrder.setSampleName(StringUtils.isNotBlank(order.getSampleView()) ? order.getSampleView() : order.getSample());
            existingOrder.setProduction(order.getProduction());
            existingOrder.setQuantity(order.getTestQuantity());
@@ -49,6 +61,7 @@
        InspectionOrder inspectionOrder = new InspectionOrder();
        inspectionOrder.setInsOrderId(order.getId());
        inspectionOrder.setEntrustCode(order.getEntrustCode());
        inspectionOrder.setBatchNo(order.getBatchNo());
        // è¯•样名称优先取报告展示字段
        inspectionOrder.setSampleName(StringUtils.isNotBlank(order.getSampleView())
                ? order.getSampleView() : order.getSample());
@@ -77,5 +90,35 @@
        }
        // æŠ¥å‘Šæ–‡ä»¶ä¸Žçº¦å®šæ—¶é—´ç”Ÿæˆæ—¶ä¸ºç©ºï¼ŒåŽç»­åœ¨æ£€éªŒå§”托单页面编辑/上传
        inspectionOrderMapper.insert(inspectionOrder);
        createDetailRow(inspectionOrder, order);
    }
    /**
     * ä¸‹å•即生成一行样品明细,批量下单后整批样品直接汇总在同一张委托书里。
     */
    private void createDetailRow(InspectionOrder inspectionOrder, InsOrder order) {
        InspectionOrderDetail detail = new InspectionOrderDetail();
        detail.setInspectionOrderId(inspectionOrder.getInspectionOrderId());
        detail.setSampleName(inspectionOrder.getSampleName());
        detail.setQuantity(order.getTestQuantity());
        detail.setSampleStatus(inspectionOrder.getSampleStatus());
        detail.setTestStandard(order.getSample());
        List<InsSample> samples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery()
                .eq(InsSample::getInsOrderId, order.getId())
                .orderByAsc(InsSample::getId));
        if (!samples.isEmpty()) {
            detail.setSampleNumber(samples.get(0).getSampleCode());
        }
        String testItem = insOrderStandardTemplateMapper.selectList(
                        Wrappers.<InsOrderStandardTemplate>lambdaQuery()
                                .eq(InsOrderStandardTemplate::getInsOrderId, order.getId())
                                .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId))
                .stream()
                .map(InsOrderStandardTemplate::getName)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.joining("、"));
        detail.setTestItem(testItem);
        inspectionOrderDetailMapper.insert(detail);
    }
}
cnas-process/src/main/java/com/ruoyi/process/pojo/InspectionOrder.java
@@ -31,6 +31,9 @@
    @Schema(description = "委托编号")
    private String entrustCode;
    @Schema(description = "批量下单批次号")
    private String batchNo;
    @Schema(description = "试样名称")
    private String sampleName;
@@ -58,6 +61,9 @@
    @Schema(description = "样品状态")
    private String sampleStatus;
    @Schema(description = "检测来源 1送样 2采样")
    private Integer sampleSource;
    @Schema(description = "是否留样1: æ˜¯, 2:否")
    private Integer isLeave;
@@ -82,6 +88,12 @@
    @Schema(description = "接收日期")
    private LocalDate receiptData;
    @Schema(description = "接收样品状态 1符合要求 2有缺陷但不影响检测 3不符合检测要求")
    private Integer receiveStatus;
    @Schema(description = "检测费 1已收 2未收 3预收")
    private Integer feeStatus;
    @Schema(description = "领样人员")
    private String sampleTakerUser;
cnas-process/src/main/java/com/ruoyi/process/pojo/InspectionOrderDetail.java
@@ -27,11 +27,20 @@
    @Schema(description = "委托单id")
    private Integer inspectionOrderId;
    @Schema(description = "样品名称")
    private String sampleName;
    @Schema(description = "样品编号")
    private String sampleNumber;
    @Schema(description = "试验项目")
    private String testItem;
    @Schema(description = "样品数量")
    private String quantity;
    @Schema(description = "样品状态")
    private String sampleStatus;
    @Schema(description = "试验依据")
    private String testStandard;
@@ -58,4 +67,8 @@
    // å¯¼å‡ºä½¿ç”¨
    @TableField(select = false, exist = false)
    private Integer index;
    // åˆå¹¶å±•示时显示该行实际所属委托单的委托编号
    @TableField(select = false, exist = false)
    private String entrustCode;
}
cnas-process/src/main/java/com/ruoyi/process/service/InspectionOrderService.java
@@ -50,11 +50,21 @@
    boolean delInspectionOrder(Integer inspectionOrderId);
    /**
     * æŒ‰æ‰¹æ¬¡åˆ é™¤æ•´æ‰¹æ£€éªŒå§”托单
     */
    boolean delInspectionOrder(Integer inspectionOrderId, String batchNo);
    /**
     * æ£€éªŒå§”托单查看详情
     * @param inspectionOrderId
     * @return
     */
    InspectionOrderDto getInspectionOrderOne(Integer inspectionOrderId);
    /**
     * æŒ‰æ‰¹æ¬¡æŸ¥çœ‹åˆå¹¶åŽçš„æ£€éªŒå§”托单
     */
    InspectionOrderDto getInspectionOrderOne(Integer inspectionOrderId, String batchNo);
    /**
     * æ ¹æ®æˆå“è®¢å•id查询委托单信息
@@ -83,4 +93,9 @@
     * @param response
     */
    void exportInspectionOrder(Integer inspectionOrderId, HttpServletResponse response);
    /**
     * æŒ‰æ‰¹æ¬¡å¯¼å‡ºåˆå¹¶åŽçš„æ£€éªŒå§”托单
     */
    void exportInspectionOrder(Integer inspectionOrderId, String batchNo, HttpServletResponse response);
}
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;
    }
}
cnas-process/src/main/resources/mapper/InspectionOrderDetailMapper.xml
@@ -6,8 +6,11 @@
    <resultMap id="BaseResultMap" type="com.ruoyi.process.pojo.InspectionOrderDetail">
        <id column="inspection_order_detail_id" property="inspectionOrderDetailId" />
        <result column="inspection_order_id" property="inspectionOrderId" />
        <result column="sample_name" property="sampleName" />
        <result column="sample_number" property="sampleNumber" />
        <result column="test_item" property="testItem" />
        <result column="quantity" property="quantity" />
        <result column="sample_status" property="sampleStatus" />
        <result column="test_standard" property="testStandard" />
        <result column="remark" property="remark" />
        <result column="create_user" property="createUser" />
cnas-process/src/main/resources/mapper/InspectionOrderMapper.xml
@@ -4,7 +4,9 @@
    <!--检验委托单分页查询-->
    <select id="pageInspectionOrder" resultType="com.ruoyi.process.dto.InspectionOrderDto">
        select *
        select a.*,
        (select count(*) from cnas_inspection_order_detail d
        where d.inspection_order_id = a.inspection_order_id) as detail_count
        from (select *
        from cnas_inspection_order
        order by create_time desc) a
cnas-process/src/main/resources/static/inspection-order.docx
Binary files differ
inspect-server/src/main/java/com/ruoyi/inspect/controller/InsOrderController.java
@@ -92,6 +92,13 @@
        return Result.success(insOrderService.addInsOrder(insPlaceOrderDto.getSampleList(), insPlaceOrderDto.getInsOrder()));
    }
    @Operation(summary = "批量添加新聚检验下单数据")
    @PreAuthorize("@ss.hasPermi('add:insOrder')")
    @PostMapping("/addBatchNewJuInsOrder")
    public Result<?> addBatchNewJuInsOrder(@RequestBody InsBatchPlaceOrderDto insBatchPlaceOrderDto) {
        return Result.success(insOrderService.addBatchNewJuInsOrder(insBatchPlaceOrderDto));
    }
    @Operation(summary = "修改新聚检验下单数据")
    @PostMapping("/updateNewJuInsOrder")
    public Result<?> updateNewJuInsOrder(@RequestBody InsPlaceOrderDto insPlaceOrderDto) {
inspect-server/src/main/java/com/ruoyi/inspect/dto/InsBatchPlaceOrderDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package com.ruoyi.inspect.dto;
import com.ruoyi.inspect.pojo.InsOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
 * æ‰¹é‡ä¸‹å•:公共委托信息 + å¤šä¸ªæ ·å“ï¼ŒåŽå°æŒ‰æ ·å“æ‹†æˆå¤šå¼ å§”托单。
 */
@Data
public class InsBatchPlaceOrderDto {
    @Schema(description = "公共委托信息")
    private InsOrder insOrder;
    @Schema(description = "样品列表,每个样品一条委托单")
    private List<InsBatchSampleDto> sampleList;
}
inspect-server/src/main/java/com/ruoyi/inspect/dto/InsBatchSampleDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.inspect.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
 * æ‰¹é‡ä¸‹å•中的单个样品。
 */
@Data
public class InsBatchSampleDto {
    @Schema(description = "样品编号")
    private String sampleCode;
    @Schema(description = "样品名称")
    private String sampleView;
    @Schema(description = "样品数量")
    private String testQuantity;
    @Schema(description = "样品状态")
    private String sampleStatus;
    @Schema(description = "标准库二级标准ID")
    private Integer standardTreeId;
    @Schema(description = "下单时保留的原始记录模板")
    private List<OrderTemplateSelectionDto> templateList;
}
inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
@@ -31,6 +31,9 @@
    @Schema(description = "委托编号")
    private String entrustCode;
    @Schema(description = "批量下单批次号")
    private String batchNo;
    @Schema(description = "外部委托编号")
    private String outEntrustCode;
inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderService.java
@@ -37,6 +37,8 @@
    int addNewJuInsOrder(InsPlaceOrderDto insPlaceOrderDto);
    int addBatchNewJuInsOrder(InsBatchPlaceOrderDto insBatchPlaceOrderDto);
    int updateNewJuInsOrder(InsPlaceOrderDto insPlaceOrderDto);
    Map<String, Object> getInsOrder(Integer id);
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderServiceImpl.java
@@ -507,6 +507,64 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addNewJuInsOrder(InsPlaceOrderDto dto) {
        return createNewJuOrder(dto, null, false);
    }
    /**
     * æ‰¹é‡ä¸‹å•:公共委托信息只填一次,后台按样品逐条拆成独立委托单并共享批次号。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int addBatchNewJuInsOrder(InsBatchPlaceOrderDto dto) {
        if (dto == null || dto.getInsOrder() == null) {
            throw new ErrorException("下单信息不能为空");
        }
        List<InsBatchSampleDto> rows = dto.getSampleList();
        if (CollectionUtils.isEmpty(rows)) {
            throw new ErrorException("请至少添加一个样品");
        }
        // åªæœ‰ä¸€ä¸ªæ ·å“æ—¶æŒ‰æ™®é€šå•样品下单处理,不产生批次
        if (rows.size() == 1) {
            return createNewJuOrder(buildBatchRowOrder(dto.getInsOrder(), rows.get(0)), null, false);
        }
        String batchNo = "PC" + DateUtil.format(new Date(), "yyyyMMddHHmmssSSS");
        int firstOrderId = 0;
        for (InsBatchSampleDto row : rows) {
            int orderId = createNewJuOrder(buildBatchRowOrder(dto.getInsOrder(), row), batchNo, true);
            if (firstOrderId == 0) {
                firstOrderId = orderId;
            }
        }
        return firstOrderId;
    }
    private InsPlaceOrderDto buildBatchRowOrder(InsOrder common, InsBatchSampleDto row) {
        if (common == null || row == null) {
            throw new ErrorException("下单信息不能为空");
        }
        if (row.getStandardTreeId() == null) {
            throw new ErrorException("请为每个样品选择检测依据");
        }
        if (StringUtils.isBlank(row.getSampleCode()) || row.getSampleCode().trim().length() < 2) {
            throw new ErrorException("样品编号不能为空且不能少于2位");
        }
        InsOrder order = new InsOrder();
        BeanUtils.copyProperties(common, order);
        order.setId(null);
        order.setEntrustCode(null);
        order.setBatchNo(null);
        order.setSampleView(row.getSampleView());
        order.setTestQuantity(row.getTestQuantity());
        order.setSampleStatus(row.getSampleStatus());
        InsPlaceOrderDto single = new InsPlaceOrderDto();
        single.setInsOrder(order);
        single.setStandardTreeId(row.getStandardTreeId());
        single.setSampleCode(row.getSampleCode());
        single.setTemplateList(row.getTemplateList());
        return single;
    }
    private int createNewJuOrder(InsPlaceOrderDto dto, String batchNo, boolean batchEntrustFallback) {
        if (dto == null || dto.getInsOrder() == null || dto.getStandardTreeId() == null) {
            throw new ErrorException("下单信息或标准不能为空");
        }
@@ -598,6 +656,7 @@
        InsOrder order = dto.getInsOrder();
        order.setId(null);
        order.setBatchNo(StringUtils.isBlank(batchNo) ? null : batchNo);
        // æ–°èšä¸‹å•免审核:直接受理,下单即流转到样品接收
        order.setState(1);
        order.setExamineTime(LocalDateTime.now());
@@ -658,6 +717,10 @@
        // ä¸‹å•时生成委托编号并校验唯一性,重复则提示,避免留到审核才报错。
        String entrustCode = buildEntrustCode(order, Collections.singletonList(sample));
        if (batchEntrustFallback && isEntrustCodeUsed(entrustCode)) {
            // æ‰¹é‡ä¸‹å•时不同样品编号末两位可能相同,改用当天顺延编号兜底
            entrustCode = buildNextAvailableEntrustCode(order);
        }
        assertEntrustCodeUnique(entrustCode, order.getId());
        insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
                .eq(InsOrder::getId, order.getId())
@@ -1050,6 +1113,23 @@
        }
    }
    private boolean isEntrustCodeUsed(String entrustCode) {
        return StringUtils.isNotBlank(entrustCode) && insOrderMapper.selectCount(Wrappers.<InsOrder>lambdaQuery()
                .eq(InsOrder::getEntrustCode, entrustCode)) > 0;
    }
    private String buildNextAvailableEntrustCode(InsOrder order) {
        LocalDate entrustDate = order.getSampleDate() == null ? LocalDate.now() : order.getSampleDate();
        String prefix = "LMSY-" + entrustDate.format(DateTimeFormatter.BASIC_ISO_DATE) + "-";
        for (int seq = 1; seq <= 99; seq++) {
            String candidate = prefix + String.format("%02d", seq);
            if (!isEntrustCodeUsed(candidate)) {
                return candidate;
            }
        }
        throw new ErrorException("当天委托编号已用尽,请调整样品编号");
    }
    /**
     * ç”Ÿæˆå§”托编号:新聚直绑模板订单末两位取样品编号末两位,其余按送样日期生成序号。
     */
sql/lims_delta.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,3 @@
-- æ‰¹é‡ä¸‹å•批次号:同一次批量下单的样品共享一个批次号
ALTER TABLE ins_order ADD COLUMN batch_no VARCHAR(64) NULL COMMENT '批量下单批次号' AFTER entrust_code;
ALTER TABLE cnas_inspection_order ADD COLUMN batch_no VARCHAR(64) NULL COMMENT '批量下单批次号' AFTER entrust_code;