cnas-process/src/main/java/com/ruoyi/process/service/impl/InspectionOrderServiceImpl.java
@@ -2,7 +2,6 @@
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -10,24 +9,33 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.ruoyi.common.numgen.NumberGenerator;
import com.ruoyi.common.core.domain.entity.Custom;
import com.ruoyi.common.core.domain.entity.User;
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.exception.ErrorException;
import com.ruoyi.inspect.dto.InsOrderDeviceRecordDto;
import com.ruoyi.inspect.mapper.InsProductResultMapper;
import com.ruoyi.inspect.mapper.InsSampleMapper;
import com.ruoyi.inspect.pojo.*;
import com.ruoyi.inspect.dto.InsPlaceOrderDto;
import com.ruoyi.inspect.pojo.InsOrder;
import com.ruoyi.inspect.pojo.InsReport;
import com.ruoyi.inspect.service.InsOrderService;
import com.ruoyi.inspect.service.InsReportService;
import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
import com.ruoyi.process.dto.InspectionOrderDto;
import com.ruoyi.process.dto.InspectionOrderBindingDto;
import com.ruoyi.process.dto.InspectionOrderExportDto;
import com.ruoyi.process.mapper.InspectionOrderBindLogMapper;
import com.ruoyi.process.mapper.InspectionOrderMapper;
import com.ruoyi.process.mapper.ProcessOrderDeviceMapper;
import com.ruoyi.process.mapper.ProcessSampleMapper;
import com.ruoyi.process.pojo.*;
import com.ruoyi.process.mapper.InspectionOrderAttachmentMapper;
import com.ruoyi.process.mapper.InspectionOrderBindingMapper;
import com.ruoyi.process.pojo.InspectionOrder;
import com.ruoyi.process.pojo.InspectionOrderAttachment;
import com.ruoyi.process.pojo.InspectionOrderBinding;
import com.ruoyi.process.pojo.InspectionOrderBindLog;
import com.ruoyi.process.pojo.InspectionOrderDetail;
import com.ruoyi.process.service.*;
import com.ruoyi.system.mapper.UserMapper;
import com.ruoyi.system.mapper.CustomMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@@ -44,11 +52,16 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Objects;
import java.util.UUID;
/**
 * <p>
@@ -62,35 +75,30 @@
@Transactional(rollbackFor = Exception.class)
public class InspectionOrderServiceImpl extends ServiceImpl<InspectionOrderMapper, InspectionOrder> implements InspectionOrderService {
    @Resource
    private InspectionOrderBindLogMapper inspectionOrderBindLogMapper;
    @Resource
    private InspectionOrderBindingMapper inspectionOrderBindingMapper;
    @Resource
    private InspectionOrderAttachmentMapper inspectionOrderAttachmentMapper;
    @Resource
    private InspectionOrderDetailService inspectionOrderDetailService;
    @Resource
    private InsOrderService insOrderService;
    @Resource
    private InsSampleMapper insSampleMapper;
    @Resource
    private InsReportService insReportService;
    @Value("${wordUrl}")
    private String wordUrl;
    @Resource
    private ProcessReportService processReportService;
    @Resource
    private ProcessOrderDeviceMapper processOrderDeviceMapper;
    @Resource
    private InsProductResultMapper insProductResultMapper;
    @Resource
    private ProcessOrderDeviceService processOrderDeviceService;
    @Resource
    private UserMapper userMapper;
    @Resource
    private ProcessSampleService processSampleService;
    private CustomMapper customMapper;
    @Resource
    private ProcessSampleMapper processSampleMapper;
    @Resource
    private ProcessDealService processDealService;
    private NumberGenerator<InspectionOrder> numberGenerator;
    /**
     * 检验委托单分页查询
     *
     * @param page
     * @param InspectionOrder
     * @return
@@ -102,39 +110,23 @@
    /**
     * 检验委托单新增
     *
     * @param inspectionOrder
     * @return
     */
    @Override
    public boolean addInspectionOrder(InspectionOrderDto inspectionOrder) {
        if (inspectionOrder.getInsOrderId() == null) {
            throw new ErrorException("缺少订单id");
    public InspectionOrderDto addInspectionOrder(InspectionOrderDto inspectionOrder) {
        if (inspectionOrder.getInsOrderId() != null) {
            throw new ErrorException("新增检验委托单不允许直接绑定成品下单,请在成品下单页面选择委托单");
        }
        normalizeCommissionParty(inspectionOrder);
        // 复制报告
        // 查询订单报告
        InsReport insReport = insReportService.getOne(Wrappers.<InsReport>lambdaQuery()
                .eq(InsReport::getInsOrderId, inspectionOrder.getInsOrderId()));
        String path = wordUrl + insReport.getUrl().replaceFirst("/word", "");
        String fileName =LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss")) + "_" + "委托单报告.docx";
        // 源文件路径
        Path sourcePath = Paths.get(path);
        // 目标文件路径
        Path targetPath = Paths.get(wordUrl + "/" + fileName);
        try {
            // 复制文件,如果目标文件已存在,则覆盖
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            // 添加文件地址
            inspectionOrder.setFileUrl("/word/" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        inspectionOrder.setInspectionOrderCode(numberGenerator.generateNumberWithPrefix(4,
                "WTD" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")),
                InspectionOrder::getInspectionOrderCode));
        // 委托编号由成品下单流程生成,委托单新增阶段不得手工填写。
        inspectionOrder.setEntrustCode(null);
        inspectionOrder.setFileUrl(null);
        baseMapper.insert(inspectionOrder);
        // 新增详情
        if (CollectionUtils.isNotEmpty(inspectionOrder.getOrderDetailList())) {
@@ -143,97 +135,34 @@
            }
            inspectionOrderDetailService.saveBatch(inspectionOrder.getOrderDetailList());
        }
        /*新增7.8报告结果*/
        ProcessReport processReport = new ProcessReport();
        processReport.setInspectionOrderId(inspectionOrder.getInsOrderId());
        processReport.setInsReportCode(inspectionOrder.getEntrustCode());//报告编号=委托编号
        //页数
        try {
            com.aspose.words.Document doc = new com.aspose.words.Document(path);
            processReport.setPages(doc.getPageCount()+"");
        } catch (Exception e) {
        }
        processReport.setNumber("1");//发送份数默认1
        processReport.setSend(inspectionOrder.getCommissionUnit());//发往何处=委托单位
        processReport.setMethod(inspectionOrder.getSend()==1?"自取":"其他");//发送方式
        processReport.setSendTime(insReport.getRatifyTime().toLocalDate());//发送日期
        processReport.setSendUser(64);//发送人固定
        processReport.setSignatory(inspectionOrder.getCommissionUser());//签收人=委托人
        processReportService.save(processReport);
        /*新增7.1委托单对应的设备使用记录*/
        addDeviceRecord(inspectionOrder,insReport.getWriteUserId());
        /*新增7.4样品接收*/
        List<InsSample> insSamples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery()
                .eq(InsSample::getInsOrderId, inspectionOrder.getInsOrderId()));
        if (insSamples.size()>0){
            List<ProcessSample> processSamples = new ArrayList<>();
            for (InsSample insSample : insSamples) {
                ProcessSample processSample = new ProcessSample();
                processSample.setReceiveDate(inspectionOrder.getSampleData());//收样日期=领样日期
                processSample.setSampleCode(insSample.getSampleCode());//样品编号
                processSample.setSampleName(insSample.getSample());//样品名称
                processSample.setNum(1);//样品数量=1
                processSample.setSampleSupplier(inspectionOrder.getCommissionUnit());//来样单位
                LocalDate plusMonths = inspectionOrder.getSampleData() == null ? null : inspectionOrder.getSampleData().plusMonths(1);
                processSample.setLeaveDate(plusMonths);//留样日期=收样日期往后延一个月
                processSample.setSampleState(inspectionOrder.getSampleStatus());//样品状态
                processSample.setDealTime(plusMonths);//退样日期=留样日期
                processSample.setInspectionOrderId(inspectionOrder.getInspectionOrderId());//委托单id
                processSamples.add(processSample);
            }
            processSampleService.saveBatch(processSamples);
        }
        return true;
    }
    private synchronized void addDeviceRecord(InspectionOrder inspectionOrder,Integer userId) {
        // 查询设备使用记录查询该订单的使用记录
        Set<String> recordCodeset = processOrderDeviceMapper.selectDeviceNumber(inspectionOrder.getInspectionOrderId());
        // 获取订单设备编号
        List<InsProductResult> resultList = insProductResultMapper.selectResultByOrderId(inspectionOrder.getInsOrderId());
        Set<String> deviceCodeSet = new HashSet<>();
        for (InsProductResult result : resultList) {
            // 添加设备编号
            List<JSONObject> jsonObjects = JSON.parseArray(result.getEquipValue(), JSONObject.class);
            for (JSONObject jsonObject : jsonObjects) {
                if (!"".equals(jsonObject.get("v") + "")) {
                    List<String> v = StrUtil.split(jsonObject.get("v") + "", ",");
                    deviceCodeSet.addAll(v);
                }
            }
        }
        // 1.判断是否有没有添加的使用记录
        Set<String> orderDeviceNumbers = getDeviceDifference(deviceCodeSet, recordCodeset);
        // 添加使用记录, 根据编号查询设备id
        if (CollectionUtils.isNotEmpty(orderDeviceNumbers)) {
            List<Integer> orderDeviceIds = processOrderDeviceMapper.selectDeviceIdsByNumbers(orderDeviceNumbers);
            List<ProcessOrderDevice> collect = orderDeviceIds.stream().map(deviceId -> {
                ProcessOrderDevice processOrderDevice = new ProcessOrderDevice();
                processOrderDevice.setInspectionOrderId(inspectionOrder.getInspectionOrderId());
                processOrderDevice.setDeviceId(deviceId);
                processOrderDevice.setSampleCode(inspectionOrder.getEntrustCode());
                processOrderDevice.setUseBefore(1);
                processOrderDevice.setUseAfter(1);
                processOrderDevice.setUsePerson(userMapper.selectById(userId).getName());//使用人
                processOrderDevice.setUsePersonId(userId);//使用人id
                return processOrderDevice;
            }).collect(Collectors.toList());
            processOrderDeviceService.saveBatch(collect);
        }
    }
    private static Set<String> getDeviceDifference(Set<String> number1, Set<String> number2) {
        return number1.stream().filter(s1 -> number2.stream().noneMatch(s2 -> s1.equals(s2)))
                .collect(Collectors.toSet());
        return inspectionOrder;
    }
    /**
     * 检验委托单修改
     *
     * @param InspectionOrder
     * @return
     */
    @Override
    public boolean updateInspectionOrder(InspectionOrderDto InspectionOrder) {
        InspectionOrder oldInspectionOrder = baseMapper.selectByIdForUpdate(InspectionOrder.getInspectionOrderId());
        if (oldInspectionOrder == null) {
            throw new ErrorException("检验委托单不存在");
        }
        Long bindingHistoryCount = inspectionOrderBindingMapper.selectCount(Wrappers.<InspectionOrderBinding>lambdaQuery()
                .eq(InspectionOrderBinding::getInspectionOrderId, InspectionOrder.getInspectionOrderId()));
        if (bindingHistoryCount > 0 && !Objects.equals(oldInspectionOrder.getCommissionUnitId(), InspectionOrder.getCommissionUnitId())) {
            throw new ErrorException("已有下单记录,不允许修改委托单位");
        }
        normalizeCommissionParty(InspectionOrder);
        // 编号、关联关系、正式委托编号和报告地址只能由专用流程维护。
        InspectionOrder.setInspectionOrderCode(null);
        InspectionOrder.setInsOrderId(null);
        InspectionOrder.setEntrustCode(null);
        InspectionOrder.setFileUrl(null);
        InspectionOrder.setSampleTakerUserId(null);
        InspectionOrder.setSampleTakerUser(null);
        baseMapper.updateById(InspectionOrder);
        // 删除之前的详情
@@ -241,57 +170,57 @@
                .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报告结果
        ProcessReport processReport = processReportService.getOne(Wrappers.<ProcessReport>lambdaQuery().eq(ProcessReport::getInspectionOrderId, InspectionOrder.getInspectionOrderId()));
        processReport.setInsReportCode(InspectionOrder.getEntrustCode());//报告编号=委托编号
        processReportService.updateById(processReport);
        return true;
    }
    /**
     * 检验委托单删除
     *
     * @param inspectionOrderId
     * @return
     */
    @Override
    public boolean delInspectionOrder(Integer inspectionOrderId) {
        InspectionOrder inspectionOrder = baseMapper.selectByIdForUpdate(inspectionOrderId);
        if (inspectionOrder == null) {
            throw new ErrorException("检验委托单不存在");
        }
        Long bindingHistoryCount = inspectionOrderBindingMapper.selectCount(Wrappers.<InspectionOrderBinding>lambdaQuery()
                .eq(InspectionOrderBinding::getInspectionOrderId, inspectionOrderId));
        Long bindLogCount = inspectionOrderBindLogMapper.selectCount(Wrappers.<InspectionOrderBindLog>lambdaQuery()
                .and(wrapper -> wrapper.eq(InspectionOrderBindLog::getOldInspectionOrderId, inspectionOrderId)
                        .or().eq(InspectionOrderBindLog::getNewInspectionOrderId, inspectionOrderId)));
        Long attachmentCount = inspectionOrderAttachmentMapper.selectCount(Wrappers.<InspectionOrderAttachment>lambdaQuery()
                .eq(InspectionOrderAttachment::getInspectionOrderId, inspectionOrderId));
        if (bindingHistoryCount > 0 || bindLogCount > 0 || attachmentCount > 0) {
            throw new ErrorException("该检验委托单已有下单或附件历史,不允许删除");
        }
        inspectionOrderDetailService.remove(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId));
        baseMapper.deleteById(inspectionOrderId);
        //删除7.8报告
        processReportService.remove(Wrappers.<ProcessReport>lambdaQuery()
                .eq(ProcessReport::getInspectionOrderId, inspectionOrderId));
        //删除对应的设备使用记录
        processOrderDeviceService.remove(Wrappers.<ProcessOrderDevice>lambdaQuery()
                .eq(ProcessOrderDevice::getInspectionOrderId, inspectionOrderId));
        //删除对应的7.4的样品申请
        List<ProcessSample> processSamples = processSampleMapper.selectList(Wrappers.<ProcessSample>lambdaQuery()
                .eq(ProcessSample::getInspectionOrderId, inspectionOrderId));
        if (processSamples.size()>0){
            List<Integer> list = processSamples.stream().map(ProcessSample::getId).collect(Collectors.toList());
            processDealService.remove(Wrappers.<ProcessDeal>lambdaQuery()
                    .in(ProcessDeal::getProcessSampleId,list));
        }
        //删除对应7.4的样品接收
        processSampleService.remove(Wrappers.<ProcessSample>lambdaQuery()
                .eq(ProcessSample::getInspectionOrderId,inspectionOrderId));
        return true;
    }
    /**
     * 检验委托单查看详情
     *
     * @param inspectionOrderId
     * @return
     */
    @Override
    public InspectionOrderDto getInspectionOrderOne(Integer inspectionOrderId) {
        InspectionOrder inspectionOrder = baseMapper.selectById(inspectionOrderId);
        if (inspectionOrder == null) {
            throw new ErrorException("检验委托单不存在");
        }
        InspectionOrderDto inspectionOrderDto = new InspectionOrderDto();
        BeanUtils.copyProperties(inspectionOrder, inspectionOrderDto);
@@ -301,77 +230,364 @@
        return inspectionOrderDto;
    }
    /**
     * 根据成品订单id查询委托单详情
     * @param insOrderId
     * @return
     */
    @Override
    public InspectionOrderDto getInspectionOrderByInsOderId(Integer insOrderId) {
        InsOrder order = insOrderService.getById(insOrderId);
        InspectionOrderDto inspectionOrderDto = new InspectionOrderDto();
        inspectionOrderDto.setInsOrderId(order.getId());//成品订单id
        inspectionOrderDto.setEntrustCode(order.getEntrustCode());//委托编号
        inspectionOrderDto.setSampleName(order.getSample());//试样名称
        inspectionOrderDto.setProduction(order.getProduction());//生产单位
        inspectionOrderDto.setCommissionUnit(order.getCompany());//委托单位
        inspectionOrderDto.setCommissionUser(order.getPrepareUser());//委托人
        inspectionOrderDto.setSampleStatus("完好");//样品状态
        inspectionOrderDto.setIsLeave(order.getIsLeave());//是否留样
        inspectionOrderDto.setProcessing(order.getProcessing());//样品处理方式
        inspectionOrderDto.setAppointed(order.getAppointed());//约定时间
        inspectionOrderDto.setSend(order.getSend());//报告发送方式
        inspectionOrderDto.setCommissionPhone(order.getPhone());//报告发送方式
        return inspectionOrderDto;
    public IPage<InspectionOrder> pageUnboundInspectionOrder(Page page, InspectionOrder inspectionOrder) {
        return baseMapper.pageUnboundInspectionOrder(page, QueryWrappers.queryWrappers(inspectionOrder));
    }
    /**
     * 委托单查询成品订单
     * @return
     */
    @Override
    public IPage<InsOrder> getInsOrderOnInspection(Page page, InsOrder insOrder) {
        return baseMapper.getInsOrderOnInspection(page, QueryWrappers.queryWrappers(insOrder));
    public IPage<InspectionOrderBindingDto> pageInspectionOrderBindings(Page page, Integer inspectionOrderId,
                                                                         Boolean includeDeleted) {
        if (inspectionOrderId == null || baseMapper.selectById(inspectionOrderId) == null) {
            throw new ErrorException("检验委托单不存在");
        }
        return inspectionOrderBindingMapper.pageBindings(page, inspectionOrderId, includeDeleted);
    }
    @Override
    public List<InspectionOrderAttachment> listInspectionOrderAttachments(Integer inspectionOrderId, Long bindingId) {
        if (inspectionOrderId == null || baseMapper.selectById(inspectionOrderId) == null) {
            throw new ErrorException("检验委托单不存在");
        }
        if (bindingId != null) {
            InspectionOrderBinding binding = inspectionOrderBindingMapper.selectById(bindingId);
            if (binding == null || !Objects.equals(binding.getInspectionOrderId(), inspectionOrderId)) {
                throw new ErrorException("下单记录不属于该检验委托单");
            }
        }
        return inspectionOrderAttachmentMapper.selectVisibleAttachments(inspectionOrderId, bindingId);
    }
    @Override
    public InspectionOrderDto getInspectionOrderForPlaceOrder(Integer inspectionOrderId) {
        InspectionOrder inspectionOrder = baseMapper.selectById(inspectionOrderId);
        if (inspectionOrder == null) {
            throw new ErrorException("检验委托单不存在");
        }
        return getInspectionOrderOne(inspectionOrderId);
    }
    @Override
    public Integer addInsOrderWithInspectionOrder(InsPlaceOrderDto insPlaceOrderDto) {
        if (insPlaceOrderDto == null || insPlaceOrderDto.getInspectionOrderId() == null) {
            throw new ErrorException("请选择检验委托单");
        }
        if (insPlaceOrderDto.getInsOrder() == null) {
            throw new ErrorException("缺少成品下单信息");
        }
        if (CollectionUtils.isEmpty(insPlaceOrderDto.getSampleList())) {
            throw new ErrorException("请填写本次样品");
        }
        String requestId = normalizeRequestId(insPlaceOrderDto.getRequestId());
        Integer requestUserId = SecurityUtils.getUserId().intValue();
        String requestHash = calculateRequestHash(insPlaceOrderDto);
        if (inspectionOrderBindingMapper.lockRequestUser(requestUserId) == null) {
            throw new ErrorException("当前操作人不存在");
        }
        InspectionOrderBinding existing = inspectionOrderBindingMapper.selectByRequest(requestUserId, requestId);
        if (existing != null) {
            return resolveIdempotentRequest(existing, requestHash);
        }
        InspectionOrder inspectionOrder = baseMapper.selectByIdForUpdate(insPlaceOrderDto.getInspectionOrderId());
        if (inspectionOrder == null) {
            throw new ErrorException("检验委托单不存在");
        }
        existing = inspectionOrderBindingMapper.selectByRequest(requestUserId, requestId);
        if (existing != null) {
            return resolveIdempotentRequest(existing, requestHash);
        }
        if (inspectionOrder.getCommissionUnitId() == null) {
            throw new ErrorException("请先完善委托单位");
        }
        if (inspectionOrder.getCommissionUserId() == null) {
            throw new ErrorException("请先完善委托人");
        }
        fillInsOrderFromInspectionOrder(inspectionOrder, insPlaceOrderDto.getInsOrder());
        Integer insOrderId = insOrderService.addInsOrder(insPlaceOrderDto.getSampleList(), insPlaceOrderDto.getInsOrder());
        Long sampleTakerUserId = SecurityUtils.getUserId();
        User sampleTaker = sampleTakerUserId == null ? null : userMapper.selectById(sampleTakerUserId);
        InspectionOrderBinding binding = new InspectionOrderBinding();
        binding.setInspectionOrderId(inspectionOrder.getInspectionOrderId());
        binding.setInsOrderId(insOrderId);
        binding.setSourceType("NEW");
        binding.setRequestId(requestId);
        binding.setRequestHash(requestHash);
        binding.setRequestUserId(requestUserId);
        binding.setSampleTakerUserId(sampleTakerUserId == null ? null : sampleTakerUserId.intValue());
        binding.setSampleTakerUser(sampleTaker == null ? null : sampleTaker.getName());
        binding.setSampleData(LocalDate.now());
        binding.setOrderDeleted(0);
        binding.setCreateUser(requestUserId);
        binding.setCreateTime(LocalDateTime.now());
        inspectionOrderBindingMapper.insert(binding);
        return insOrderId;
    }
    private String normalizeRequestId(String requestId) {
        if (StrUtil.isBlank(requestId)) {
            throw new ErrorException("缺少下单请求号,请刷新页面后重试");
        }
        try {
            return UUID.fromString(requestId.trim()).toString();
        } catch (IllegalArgumentException e) {
            throw new ErrorException("下单请求号格式错误,请刷新页面后重试");
        }
    }
    private Integer resolveIdempotentRequest(InspectionOrderBinding existing, String requestHash) {
        if (!Objects.equals(existing.getRequestHash(), requestHash)) {
            throw new ErrorException("请求号已被其他下单内容使用,请重新发起下单");
        }
        if (Integer.valueOf(1).equals(existing.getOrderDeleted())) {
            throw new ErrorException("原订单已删除,请重新发起下单");
        }
        return existing.getInsOrderId();
    }
    private String calculateRequestHash(InsPlaceOrderDto dto) {
        LinkedHashMap<String, Object> content = new LinkedHashMap<>();
        content.put("inspectionOrderId", dto.getInspectionOrderId());
        content.put("insOrder", dto.getInsOrder());
        content.put("sampleList", dto.getSampleList());
        try {
            byte[] digest = MessageDigest.getInstance("SHA-256")
                    .digest(JSON.toJSONString(content).getBytes(StandardCharsets.UTF_8));
            StringBuilder result = new StringBuilder(64);
            for (byte value : digest) {
                result.append(String.format("%02x", value & 0xff));
            }
            return result.toString();
        } catch (Exception e) {
            throw new ErrorException("下单请求校验失败");
        }
    }
    private void fillInsOrderFromInspectionOrder(InspectionOrder inspectionOrder, InsOrder insOrder) {
        insOrder.setEntrustCode(null);
        insOrder.setSample(inspectionOrder.getSampleName());
        insOrder.setCompany(inspectionOrder.getCommissionUnit());
        insOrder.setCompanyId(inspectionOrder.getCommissionUnitId());
        insOrder.setProduction(inspectionOrder.getProduction());
        insOrder.setPrepareUser(inspectionOrder.getCommissionUser());
        insOrder.setPhone(inspectionOrder.getCommissionPhone());
        insOrder.setSampleStatus(inspectionOrder.getSampleStatus());
        insOrder.setIsLeave(inspectionOrder.getIsLeave());
        insOrder.setProcessing(inspectionOrder.getProcessing());
        insOrder.setAppointed(inspectionOrder.getAppointed());
        insOrder.setSend(inspectionOrder.getSend());
    }
    /** 仅信任客户和人员主数据,名称与电话均以主数据快照为准。 */
    private void normalizeCommissionParty(InspectionOrder inspectionOrder) {
        if (inspectionOrder.getCommissionUnitId() == null) {
            throw new ErrorException("请选择委托单位");
        }
        Custom custom = customMapper.selectById(inspectionOrder.getCommissionUnitId());
        if (custom == null) {
            throw new ErrorException("委托单位不存在或已停用");
        }
        inspectionOrder.setCommissionUnit(custom.getCompany());
        if (inspectionOrder.getCommissionUserId() == null) {
            throw new ErrorException("请选择委托人");
        }
        User commissionUser = userMapper.selectById(inspectionOrder.getCommissionUserId());
        if (commissionUser == null) {
            throw new ErrorException("委托人不存在或已停用");
        }
        inspectionOrder.setCommissionUser(commissionUser.getName());
        inspectionOrder.setCommissionPhone(commissionUser.getPhone());
    }
    @Override
    public void syncEntrustCode(Integer insOrderId) {
        // 委托编号属于具体成品订单,页面通过绑定关系实时读取,不再回写委托单主表。
    }
    @Override
    public void syncReportFile(Integer insReportId) {
        if (insReportId == null) {
            return;
        }
        InsReport insReport = insReportService.getById(insReportId);
        if (insReport == null || !Integer.valueOf(1).equals(insReport.getIsRatify())) {
            return;
        }
        InspectionOrderBinding binding = inspectionOrderBindingMapper.selectOne(Wrappers.<InspectionOrderBinding>lambdaQuery()
                .eq(InspectionOrderBinding::getInsOrderId, insReport.getInsOrderId())
                .eq(InspectionOrderBinding::getOrderDeleted, 0)
                .last("limit 1"));
        if (binding == null) {
            return;
        }
        InspectionOrder inspectionOrder = baseMapper.selectById(binding.getInspectionOrderId());
        if (inspectionOrder == null) return;
        String sourceUrl = StrUtil.isBlank(insReport.getUrlS()) ? insReport.getUrl() : insReport.getUrlS();
        if (StrUtil.isBlank(sourceUrl)) {
            throw new ErrorException("正式报告文件不存在,无法同步到检验委托单");
        }
        String sourceFileName = sourceUrl.substring(sourceUrl.lastIndexOf('/') + 1);
        String extension = sourceFileName.lastIndexOf('.') >= 0
                ? sourceFileName.substring(sourceFileName.lastIndexOf('.')) : ".docx";
        String fileName = inspectionOrder.getInspectionOrderCode() + "_order_" + insReport.getInsOrderId()
                + "_report_" + insReportId + "_" + UUID.randomUUID() + extension;
        Path sourcePath = Paths.get(wordUrl + sourceUrl.replaceFirst("/word", ""));
        Path targetPath = Paths.get(wordUrl, fileName);
        try {
            if (!Files.exists(sourcePath)) {
                throw new ErrorException("正式报告文件不存在,无法同步到检验委托单");
            }
            Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            InspectionOrderAttachment attachment = inspectionOrderAttachmentMapper.selectOne(
                    Wrappers.<InspectionOrderAttachment>lambdaQuery()
                            .eq(InspectionOrderAttachment::getInsReportId, insReportId).last("limit 1"));
            if (attachment == null) {
                attachment = new InspectionOrderAttachment();
                attachment.setInsReportId(insReportId);
                attachment.setSourceType("REPORT");
                attachment.setCreateUser(SecurityUtils.getUserId().intValue());
                attachment.setCreateTime(LocalDateTime.now());
            }
            attachment.setInspectionOrderId(binding.getInspectionOrderId());
            attachment.setBindingId(binding.getId());
            attachment.setFileName(fileName);
            attachment.setFileUrl("/word/" + fileName);
            attachment.setUpdateTime(LocalDateTime.now());
            if (attachment.getId() == null) {
                inspectionOrderAttachmentMapper.insert(attachment);
            } else {
                inspectionOrderAttachmentMapper.updateById(attachment);
            }
        } catch (ErrorException e) {
            throw e;
        } catch (Exception e) {
            throw new ErrorException("报告同步失败");
        }
    }
    @Override
    public boolean changeBinding(Integer insOrderId, Integer inspectionOrderId, String reason) {
        if (insOrderId == null || inspectionOrderId == null || StrUtil.isBlank(reason)) {
            throw new ErrorException("缺少变更关联所需参数");
        }
        InsOrder insOrder = insOrderService.getById(insOrderId);
        if (insOrder == null) {
            throw new ErrorException("成品下单不存在");
        }
        if (!(Integer.valueOf(0).equals(insOrder.getState()) || Integer.valueOf(2).equals(insOrder.getState()))
                || StrUtil.isNotBlank(insOrder.getEntrustCode())) {
            throw new ErrorException("当前下单已进入检验流程或已生成委托编号,不允许变更关联");
        }
        InspectionOrderBinding binding = inspectionOrderBindingMapper.selectByOrderIdForUpdate(insOrderId);
        if (binding == null || Integer.valueOf(1).equals(binding.getOrderDeleted())) {
            throw new ErrorException("成品下单未绑定有效检验委托单");
        }
        InspectionOrder target = baseMapper.selectByIdForUpdate(inspectionOrderId);
        if (target == null) {
            throw new ErrorException("目标检验委托单不存在");
        }
        if (Objects.equals(binding.getInspectionOrderId(), inspectionOrderId)) {
            throw new ErrorException("目标检验委托单与当前委托单相同");
        }
        if (!Objects.equals(target.getCommissionUnitId(), insOrder.getCompanyId())) {
            throw new ErrorException("目标检验委托单与成品订单的委托单位不一致");
        }
        Integer oldInspectionOrderId = binding.getInspectionOrderId();
        binding.setInspectionOrderId(inspectionOrderId);
        binding.setUpdateUser(SecurityUtils.getUserId().intValue());
        binding.setUpdateTime(LocalDateTime.now());
        inspectionOrderBindingMapper.updateById(binding);
        inspectionOrderAttachmentMapper.update(null, Wrappers.<InspectionOrderAttachment>lambdaUpdate()
                .eq(InspectionOrderAttachment::getBindingId, binding.getId())
                .set(InspectionOrderAttachment::getInspectionOrderId, inspectionOrderId)
                .set(InspectionOrderAttachment::getUpdateTime, LocalDateTime.now()));
        InspectionOrderBindLog bindLog = new InspectionOrderBindLog();
        bindLog.setInsOrderId(insOrderId);
        bindLog.setOldInspectionOrderId(oldInspectionOrderId);
        bindLog.setNewInspectionOrderId(inspectionOrderId);
        bindLog.setChangeReason(reason.trim());
        bindLog.setCreateUser(SecurityUtils.getUserId().intValue());
        bindLog.setCreateTime(LocalDateTime.now());
        inspectionOrderBindLogMapper.insert(bindLog);
        return true;
    }
    /**
     * 委托单成品报告上传
     *
     * @param file
     * @param inspectionOrderId
     * @return
     */
    @Override
    public boolean uploadInspectionOrderFile(MultipartFile file, Integer inspectionOrderId) {
        String urlString;
        String pathName;
        try {
            String path = wordUrl;
            File realpath = new File(path);
            if (!realpath.exists()) {
                realpath.mkdirs();
            }
            pathName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss")) + "_" + file.getOriginalFilename();
            urlString = realpath + "/" + pathName;
            file.transferTo(new File(urlString));
            baseMapper.update(null, Wrappers.<InspectionOrder>lambdaUpdate()
                    .eq(InspectionOrder::getInspectionOrderId, inspectionOrderId)
                    .set(InspectionOrder::getFileUrl, "/word/" + pathName));
        } catch (Exception e) {
            throw new ErrorException("文件上传失败");
        }
        return true;
        throw new ErrorException("请在下单记录中选择具体订单后上传附件");
    }
    @Override
    public void exportInspectionOrder(Integer inspectionOrderId, HttpServletResponse response) {
    public boolean uploadInspectionOrderBindingFile(MultipartFile file, Long bindingId) {
        if (file == null || file.isEmpty() || bindingId == null) {
            throw new ErrorException("请选择下单记录和上传文件");
        }
        InspectionOrderBinding binding = inspectionOrderBindingMapper.selectById(bindingId);
        if (binding == null || Integer.valueOf(1).equals(binding.getOrderDeleted())) {
            throw new ErrorException("下单记录不存在或订单已删除");
        }
        try {
            File realpath = new File(wordUrl);
            if (!realpath.exists() && !realpath.mkdirs()) {
                throw new ErrorException("文件目录创建失败");
            }
            String originalName = new File(file.getOriginalFilename() == null ? "attachment" : file.getOriginalFilename()).getName();
            String pathName = "binding_" + bindingId + "_" + UUID.randomUUID() + "_" + originalName;
            file.transferTo(new File(realpath, pathName));
            InspectionOrderAttachment attachment = new InspectionOrderAttachment();
            attachment.setInspectionOrderId(binding.getInspectionOrderId());
            attachment.setBindingId(bindingId);
            attachment.setSourceType("MANUAL");
            attachment.setFileName(originalName);
            attachment.setFileUrl("/word/" + pathName);
            attachment.setCreateUser(SecurityUtils.getUserId().intValue());
            attachment.setCreateTime(LocalDateTime.now());
            inspectionOrderAttachmentMapper.insert(attachment);
            return true;
        } catch (ErrorException e) {
            throw e;
        } catch (Exception e) {
            throw new ErrorException("文件上传失败");
        }
    }
    @Override
    public void exportInspectionOrder(Integer inspectionOrderId, Long bindingId, HttpServletResponse response) {
        InspectionOrder inspectionOrder = baseMapper.selectById(inspectionOrderId);
        if (inspectionOrder == null) {
            throw new ErrorException("检验委托单不存在");
        }
        InspectionOrderExportDto inspectionOrderExportDto = extracted(inspectionOrder);
        if (bindingId == null) {
            inspectionOrderExportDto.setEntrustCode(null);
            inspectionOrderExportDto.setSampleTakerUser(null);
            inspectionOrderExportDto.setSampleTakerUserId(null);
            inspectionOrderExportDto.setSampleData(null);
            inspectionOrderExportDto.setSampleDataString(null);
        } else {
            InspectionOrderBinding binding = inspectionOrderBindingMapper.selectById(bindingId);
            if (binding == null || !Objects.equals(binding.getInspectionOrderId(), inspectionOrderId)) {
                throw new ErrorException("下单记录不属于该检验委托单");
            }
            InsOrder insOrder = insOrderService.getById(binding.getInsOrderId());
            inspectionOrderExportDto.setEntrustCode(insOrder == null ? null : insOrder.getEntrustCode());
            inspectionOrderExportDto.setSampleTakerUser(binding.getSampleTakerUser());
            inspectionOrderExportDto.setSampleTakerUserId(binding.getSampleTakerUserId());
            inspectionOrderExportDto.setSampleData(binding.getSampleData());
            if (binding.getSampleData() != null) {
                inspectionOrderExportDto.setSampleDataString(binding.getSampleData()
                        .format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
            }
        }
        List<InspectionOrderDetail> list = inspectionOrderDetailService.list(Wrappers.<InspectionOrderDetail>lambdaQuery()
                .eq(InspectionOrderDetail::getInspectionOrderId, inspectionOrderId));
        int index = 1;
@@ -401,6 +617,7 @@
            template.write(os);
            os.flush();
            os.close();
            inputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("导出失败");
@@ -425,14 +642,14 @@
            inspectionOrderExportDto.setAppointedString(inspectionOrder.getAppointed().format(formatter));
        }
        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.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? "☑" : "□");
        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.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 ? "☑" : "□");
        return inspectionOrderExportDto;
    }
}