package com.ruoyi.production; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.ruoyi.basic.mapper.ProductModelMapper; import com.ruoyi.basic.pojo.ProductModel; import com.ruoyi.production.mapper.ProcessRouteItemMapper; import com.ruoyi.production.mapper.ProcessRouteMapper; import com.ruoyi.production.pojo.ProcessRoute; import com.ruoyi.production.pojo.ProcessRouteItem; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** * 工艺路线项目补齐测试。 */ @SpringBootTest public class ProcessRouteItemCopyTest { @Autowired private ProductModelMapper productModelMapper; @Autowired private ProcessRouteMapper processRouteMapper; @Autowired private ProcessRouteItemMapper processRouteItemMapper; @Test @Rollback(false) @Transactional(rollbackFor = Exception.class) public void copyAllProcessRouteItemsByProductId() { List allProductModels = productModelMapper.selectList( Wrappers.lambdaQuery() .isNotNull(ProductModel::getProductId) .orderByAsc(ProductModel::getProductId) .orderByAsc(ProductModel::getId) ); if (CollectionUtils.isEmpty(allProductModels)) { throw new IllegalStateException("未查询到任何产品规格"); } Map> productModelGroups = allProductModels.stream() .collect(Collectors.groupingBy(ProductModel::getProductId, LinkedHashMap::new, Collectors.toList())); List summaries = new ArrayList<>(); for (Map.Entry> entry : productModelGroups.entrySet()) { CopySummary summary = copyProcessRouteItems(entry.getKey(), entry.getValue()); summaries.add(summary); printProductSummary(summary); } printBatchSummary(summaries); } private CopySummary copyProcessRouteItems(Long productId, List productModels) { if (CollectionUtils.isEmpty(productModels)) { return CopySummary.skipped(productId, 0, 0, "未查询到产品大类对应的规格"); } Map productModelMap = productModels.stream() .collect(Collectors.toMap(ProductModel::getId, item -> item, (left, right) -> left, LinkedHashMap::new)); List productModelIds = new ArrayList<>(productModelMap.keySet()); List routeList = processRouteMapper.selectList( Wrappers.lambdaQuery() .in(ProcessRoute::getProductModelId, productModelIds) .orderByAsc(ProcessRoute::getId) ); if (CollectionUtils.isEmpty(routeList)) { return CopySummary.skipped(productId, productModels.size(), 0, "未查询到产品大类对应的工艺路线"); } Map> routeItemMap = loadRouteItemMap(routeList); ProcessRoute templateRoute = findTemplateRouteOrNull(routeList, routeItemMap); if (templateRoute == null) { return CopySummary.skipped(productId, productModels.size(), routeList.size(), "当前产品大类下没有可作为模板的完整路线项目"); } List templateItems = routeItemMap.getOrDefault(templateRoute.getId(), Collections.emptyList()) .stream() .sorted(Comparator .comparing(ProcessRouteItem::getDragSort, Comparator.nullsLast(Integer::compareTo)) .thenComparing(ProcessRouteItem::getId)) .collect(Collectors.toList()); int copiedRouteCount = 0; int skippedRouteCount = 0; int insertedRouteItemCount = 0; for (ProcessRoute targetRoute : routeList) { if (Objects.equals(templateRoute.getId(), targetRoute.getId())) { skippedRouteCount++; continue; } List targetItems = routeItemMap.getOrDefault(targetRoute.getId(), Collections.emptyList()); if (!targetItems.isEmpty()) { skippedRouteCount++; continue; } int copiedItems = copyTemplateItems(templateItems, targetRoute); copiedRouteCount++; insertedRouteItemCount += copiedItems; } return CopySummary.copied( productId, productModels.size(), templateRoute.getId(), templateRoute.getProcessRouteCode(), routeList.size(), copiedRouteCount, skippedRouteCount, insertedRouteItemCount ); } private Map> loadRouteItemMap(List routeList) { List routeIds = routeList.stream() .map(ProcessRoute::getId) .filter(Objects::nonNull) .collect(Collectors.toList()); if (routeIds.isEmpty()) { return Collections.emptyMap(); } List routeItems = processRouteItemMapper.selectList( Wrappers.lambdaQuery() .in(ProcessRouteItem::getRouteId, routeIds) .orderByAsc(ProcessRouteItem::getRouteId) .orderByAsc(ProcessRouteItem::getDragSort) .orderByAsc(ProcessRouteItem::getId) ); Map> routeItemMap = new HashMap<>(); for (ProcessRouteItem routeItem : routeItems) { routeItemMap.computeIfAbsent(routeItem.getRouteId(), key -> new ArrayList<>()).add(routeItem); } return routeItemMap; } private ProcessRoute findTemplateRouteOrNull(List routeList, Map> routeItemMap) { return routeList.stream() .filter(item -> routeItemCount(routeItemMap.getOrDefault(item.getId(), Collections.emptyList())) > 0) .max(Comparator.comparingInt(item -> routeItemCount(routeItemMap.getOrDefault(item.getId(), Collections.emptyList())))) .orElse(null); } private int routeItemCount(List routeItems) { return routeItems == null ? 0 : routeItems.size(); } private int copyTemplateItems(List templateItems, ProcessRoute targetRoute) { int insertedCount = 0; for (int i = 0; i < templateItems.size(); i++) { ProcessRouteItem sourceItem = templateItems.get(i); ProcessRouteItem copiedItem = new ProcessRouteItem(); copiedItem.setRouteId(targetRoute.getId()); copiedItem.setProductModelId(sourceItem.getProductModelId()); copiedItem.setProcessId(sourceItem.getProcessId()); copiedItem.setTenantId(firstNotNull(targetRoute.getTenantId(), sourceItem.getTenantId())); copiedItem.setDragSort(sourceItem.getDragSort() == null ? i + 1 : sourceItem.getDragSort()); copiedItem.setIsQuality(sourceItem.getIsQuality()); processRouteItemMapper.insert(copiedItem); insertedCount++; } return insertedCount; } private Long firstNotNull(Long first, Long second) { return first != null ? first : second; } private void printProductSummary(CopySummary summary) { if (summary.isProductSkipped()) { System.out.printf( "跳过产品分类:productId=%s,规格数=%s,工艺路线总数=%s,原因=%s%n", summary.getProductId(), summary.getProductModelCount(), summary.getTotalRouteCount(), summary.getSkipReason() ); return; } System.out.printf( "处理产品分类:productId=%s,模板工艺路线=%s(%s),规格数=%s,工艺路线总数=%s,已补齐路线=%s,跳过路线=%s,新增路线项目=%s%n", summary.getProductId(), summary.getTemplateRouteCode(), summary.getTemplateRouteId(), summary.getProductModelCount(), summary.getTotalRouteCount(), summary.getCopiedRouteCount(), summary.getSkippedRouteCount(), summary.getInsertedRouteItemCount() ); } private void printBatchSummary(List summaries) { int productCount = summaries.size(); int skippedProductCount = 0; int totalRouteCount = 0; int copiedRouteCount = 0; int skippedRouteCount = 0; int insertedRouteItemCount = 0; for (CopySummary summary : summaries) { if (summary.isProductSkipped()) { skippedProductCount++; } totalRouteCount += summary.getTotalRouteCount(); copiedRouteCount += summary.getCopiedRouteCount(); skippedRouteCount += summary.getSkippedRouteCount(); insertedRouteItemCount += summary.getInsertedRouteItemCount(); } System.out.printf( "工艺路线项目批量复制完成:产品分类数=%s,已处理分类=%s,跳过分类=%s,工艺路线总数=%s,已补齐路线=%s,跳过路线=%s,新增路线项目=%s%n", productCount, productCount - skippedProductCount, skippedProductCount, totalRouteCount, copiedRouteCount, skippedRouteCount, insertedRouteItemCount ); } private static class CopySummary { private final Long productId; private final int productModelCount; private final Long templateRouteId; private final String templateRouteCode; private final int totalRouteCount; private final int copiedRouteCount; private final int skippedRouteCount; private final int insertedRouteItemCount; private final String skipReason; private CopySummary(Long productId, int productModelCount, Long templateRouteId, String templateRouteCode, int totalRouteCount, int copiedRouteCount, int skippedRouteCount, int insertedRouteItemCount, String skipReason) { this.productId = productId; this.productModelCount = productModelCount; this.templateRouteId = templateRouteId; this.templateRouteCode = templateRouteCode; this.totalRouteCount = totalRouteCount; this.copiedRouteCount = copiedRouteCount; this.skippedRouteCount = skippedRouteCount; this.insertedRouteItemCount = insertedRouteItemCount; this.skipReason = skipReason; } private static CopySummary copied(Long productId, int productModelCount, Long templateRouteId, String templateRouteCode, int totalRouteCount, int copiedRouteCount, int skippedRouteCount, int insertedRouteItemCount) { return new CopySummary( productId, productModelCount, templateRouteId, templateRouteCode, totalRouteCount, copiedRouteCount, skippedRouteCount, insertedRouteItemCount, null ); } private static CopySummary skipped(Long productId, int productModelCount, int totalRouteCount, String skipReason) { return new CopySummary( productId, productModelCount, null, null, totalRouteCount, 0, totalRouteCount, 0, skipReason ); } private Long getProductId() { return productId; } private int getProductModelCount() { return productModelCount; } private Long getTemplateRouteId() { return templateRouteId; } private String getTemplateRouteCode() { return templateRouteCode; } private int getTotalRouteCount() { return totalRouteCount; } private int getCopiedRouteCount() { return copiedRouteCount; } private int getSkippedRouteCount() { return skippedRouteCount; } private int getInsertedRouteItemCount() { return insertedRouteItemCount; } private String getSkipReason() { return skipReason; } private boolean isProductSkipped() { return skipReason != null; } } }