package com.ruoyi.inspect.service.impl; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.basic.dto.TemplateInspectionItemDto; import com.ruoyi.basic.utils.StandardTemplateInspectionItemParser; import com.ruoyi.common.utils.GZipUtil; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.framework.exception.ErrorException; import com.ruoyi.inspect.dto.SampleReceiptActionDto; import com.ruoyi.inspect.dto.SampleReceiptPageDto; import com.ruoyi.inspect.dto.SampleReceiptQueryDto; import com.ruoyi.inspect.mapper.*; import com.ruoyi.inspect.pojo.*; import com.ruoyi.inspect.service.InsOrderService; import com.ruoyi.inspect.service.InsOrderStateService; import com.ruoyi.inspect.service.InsSampleReceiptService; import com.ruoyi.system.mapper.UserMapper; import jakarta.servlet.http.HttpServletResponse; import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.net.URLEncoder; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @Service @AllArgsConstructor public class InsSampleReceiptServiceImpl extends ServiceImpl implements InsSampleReceiptService { private static final String DIRECT_TEMPLATE_LABORATORY = "新聚"; private final InsSampleReceiptMapper receiptMapper; private final InsOrderMapper insOrderMapper; private final InsOrderStandardTemplateMapper insOrderStandardTemplateMapper; private final InsSampleMapper insSampleMapper; private final InsProductMapper insProductMapper; private final InsSampleUserMapper insSampleUserMapper; private final InsOrderStateService insOrderStateService; private final InsOrderService insOrderService; private final UserMapper userMapper; @Override public IPage page(Page page, SampleReceiptQueryDto query) { IPage result = receiptMapper.selectPage(page, query); result.getRecords().forEach(this::fillInspectionItemsFromSnapshots); return result; } @Override public SampleReceiptPageDto detail(Integer id) { SampleReceiptPageDto detail = receiptMapper.selectDetail(id); fillInspectionItemsFromSnapshots(detail); return detail; } @Override @Transactional(rollbackFor = Exception.class) public void submit(SampleReceiptActionDto dto) { if (dto == null || dto.getReceiptId() == null || dto.getMainInspectorId() == null) { throw new ErrorException("请选择主检验人"); } InsSampleReceipt receipt = receiptMapper.selectForUpdate(dto.getReceiptId()); ensurePending(receipt); InsOrder order = insOrderMapper.selectById(receipt.getInsOrderId()); if (order == null || !Integer.valueOf(1).equals(order.getState())) { throw new ErrorException("该委托当前状态不允许样品接收"); } List samples = insSampleMapper.selectList(Wrappers.lambdaQuery() .eq(InsSample::getInsOrderId, order.getId()).orderByAsc(InsSample::getId)); if (samples.size() != 1 || !samples.get(0).getId().equals(receipt.getInsSampleId())) { throw new ErrorException("该委托的样品数量异常,请检查下单数据"); } InsSample sample = samples.get(0); // 只走新聚直绑模板流程:委托单必须已绑定原始记录模板(下单时生成订单模板快照) Long snapshotCount = insOrderStandardTemplateMapper.selectCount(Wrappers.lambdaQuery() .eq(InsOrderStandardTemplate::getInsOrderId, order.getId())); if (snapshotCount == 0) { throw new ErrorException("该委托未绑定原始记录模板,不能提交检验"); } Long productCount = insProductMapper.selectCount(Wrappers.lambdaQuery() .eq(InsProduct::getInsSampleId, sample.getId()).eq(InsProduct::getState, 1)); if (productCount == 0) { List items = getSnapshotInspectionItems(order.getId()); if (items.isEmpty()) { throw new ErrorException("订单原始记录模板未配置检验项或导出值,不能提交检验"); } int sort = 0; for (TemplateInspectionItemDto item : items) { InsProduct product = new InsProduct(); product.setInsSampleId(sample.getId()); product.setInspectionItem(item.getInspectionItem()); product.setInspectionItemClass(""); product.setInspectionItemSubclass(""); product.setLaboratory(order.getLaboratory()); product.setSample(order.getSample()); product.setTemplateId(item.getTemplateId()); product.setTemplateRowIndex(item.getTemplateRowIndex()); product.setSonLaboratory(DIRECT_TEMPLATE_LABORATORY); product.setState(1); product.setSort(sort++); insProductMapper.insert(product); } } if (userMapper.selectById(dto.getMainInspectorId()) == null) { throw new ErrorException("主检验人不存在或已停用"); } if (insOrderStateService.count(Wrappers.lambdaQuery(com.ruoyi.inspect.pojo.InsOrderState.class) .eq(com.ruoyi.inspect.pojo.InsOrderState::getInsOrderId, order.getId())) > 0 || insSampleUserMapper.selectCount(Wrappers.lambdaQuery() .eq(InsSampleUser::getInsSampleId, order.getId())) > 0) { throw new ErrorException("该委托已进入检验任务,不能重复提交"); } String appointed = LocalDateTime.now().plusHours(insProductMapper.selectOrderManDay(order.getId())) .format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); insOrderService.upInsOrder(order.getId(), sample.getId(), appointed, dto.getMainInspectorId(), null); receipt.setStatus(1); receipt.setMainInspectorId(dto.getMainInspectorId()); receipt.setReceiveUserId(SecurityUtils.getUserId().intValue()); receipt.setReceiveTime(LocalDateTime.now()); receipt.setSubmitTime(LocalDateTime.now()); receiptMapper.updateById(receipt); } @Override @Transactional(rollbackFor = Exception.class) public void reject(SampleReceiptActionDto dto) { if (dto == null || dto.getReceiptId() == null || StringUtils.isBlank(dto.getRemark())) { throw new ErrorException("请填写拒收备注"); } if (dto.getRemark().trim().length() > 500) { throw new ErrorException("拒收备注不能超过500个字符"); } InsSampleReceipt receipt = receiptMapper.selectForUpdate(dto.getReceiptId()); ensurePending(receipt); if (insOrderStateService.count(Wrappers.lambdaQuery(com.ruoyi.inspect.pojo.InsOrderState.class) .eq(com.ruoyi.inspect.pojo.InsOrderState::getInsOrderId, receipt.getInsOrderId())) > 0) { throw new ErrorException("该委托已进入检验任务,不能拒收"); } receipt.setStatus(2); receipt.setRejectRemark(dto.getRemark().trim()); receipt.setReceiveUserId(SecurityUtils.getUserId().intValue()); receipt.setReceiveTime(LocalDateTime.now()); receiptMapper.updateById(receipt); InsOrder currentOrder = insOrderMapper.selectById(receipt.getInsOrderId()); if (currentOrder == null || StringUtils.isBlank(currentOrder.getEntrustCode())) { throw new ErrorException("委托编号不存在,不能作废"); } String originalCode = currentOrder.getEntrustCode(); String voidCode = originalCode + "-zf"; int suffix = 2; // 同一原编号可能经历多次下单、拒收,保留每张作废单的唯一编号。 while (insOrderMapper.selectCount(Wrappers.lambdaQuery() .eq(InsOrder::getEntrustCode, voidCode) .ne(InsOrder::getId, currentOrder.getId())) > 0) { voidCode = originalCode + "-" + suffix++ + "-zf"; } InsOrder order = new InsOrder(); order.setId(currentOrder.getId()); order.setEntrustCode(voidCode); order.setState(5); insOrderMapper.updateById(order); insOrderMapper.updateInspectionOrderEntrustCode(order.getId(), voidCode); } private void ensurePending(InsSampleReceipt receipt) { if (receipt == null || !Integer.valueOf(0).equals(receipt.getStatus())) { throw new ErrorException("该样品接收记录已被处理,请刷新后重试"); } } private List getSnapshotInspectionItems(Integer orderId) { List snapshots = insOrderStandardTemplateMapper.selectList( Wrappers.lambdaQuery() .eq(InsOrderStandardTemplate::getInsOrderId, orderId) .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId)); List items = new ArrayList<>(); for (InsOrderStandardTemplate snapshot : snapshots) { String thing = snapshot.getThing(); try { thing = GZipUtil.uncompress(thing); } catch (RuntimeException ignored) { // 兼容未压缩的历史模板快照。 } items.addAll(StandardTemplateInspectionItemParser.parse( snapshot.getTemplateId(), snapshot.getSort(), thing)); } return items; } private void fillInspectionItemsFromSnapshots(SampleReceiptPageDto row) { if (row == null || row.getOrderId() == null || StringUtils.isNotBlank(row.getInspectionItem())) { return; } try { String names = getSnapshotInspectionItems(row.getOrderId()).stream() .map(TemplateInspectionItemDto::getInspectionItem) .distinct().collect(Collectors.joining("\n")); row.setInspectionItem(names); } catch (RuntimeException ignored) { // 列表仍可查看;提交检验时会返回具体的模板解析错误。 } } @Override public void export(SampleReceiptQueryDto query, HttpServletResponse response) { List list = receiptMapper.selectExportList(query); list.forEach(this::fillInspectionItemsFromSnapshots); response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("UTF-8"); try { String fileName = URLEncoder.encode("样品接收导出", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()) .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build(); WriteSheet sheet = EasyExcel.writerSheet(0, "样品接收导出").head(SampleReceiptPageDto.class).build(); excelWriter.write(list, sheet); excelWriter.finish(); } catch (IOException e) { throw new RuntimeException("导出失败"); } } }