10 天以前 310da8ec2f87930444b62212ba513aab9e2fa878
src/main/java/com/ruoyi/common/utils/OrderUtils.java
@@ -2,6 +2,10 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ruoyi.basic.dto.ProductModelExcelDto;
import com.ruoyi.basic.dto.ProductModelExcelItemDto;
import com.ruoyi.common.utils.uuid.UUID;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.stereotype.Component;
@@ -10,9 +14,8 @@
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author :yys
@@ -51,6 +54,306 @@
        // 执行查询
        Long aLong = mapper.selectCount(queryWrapper);
        // 拼接订单编号 preFix + 时间(yyyyMMdd) + 订单数量(001)
        return preFix + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1));
        return preFix + LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE).replaceAll("-", "") + String.format("%03d", (aLong + 1)) + "-" + new Date().getTime();
    }
    /**
     * 将平面列表转换为树形结构(含规格型号拆分功能)
     * @param flatList 导入的平面数据列表
     * @return 树形结构的根节点列表
     */
    public static List<ProductModelExcelDto> buildTree(List<ProductModelExcelDto> flatList) {
        // 1. 参数校验
        if (CollectionUtils.isEmpty(flatList)) {
            return new ArrayList<>();
        }
        // 2. 预处理:过滤无效数据 + 规格型号拆分 + 初始化子节点列表
        List<ProductModelExcelDto> validList = preprocessData(flatList);
        if (CollectionUtils.isEmpty(validList)) {
            return new ArrayList<>();
        }
        // 3. 按层级分组,便于逐层处理
        Map<Integer, List<ProductModelExcelDto>> levelGroupMap = groupByLevel(validList);
        // 4. 构建节点映射(序号 -> 节点),提高查询效率
        Map<String, ProductModelExcelDto> nodeMap = buildNodeMap(validList);
        // 5. 逐层构建父子关系
        buildParentChildRelation(levelGroupMap, nodeMap);
        // 6. 获取根节点(层级1)并排序
        List<ProductModelExcelDto> rootNodes = getRootNodes(levelGroupMap);
        sortTreeNodes(rootNodes);
        return rootNodes;
    }
    /**
     * 数据预处理:
     * 1. 过滤无效数据
     * 2. 规格型号拆分并填充到items集合
     * 3. 初始化子节点列表
     */
    private static List<ProductModelExcelDto> preprocessData(List<ProductModelExcelDto> flatList) {
        return flatList.stream()
                // 过滤核心字段无效的节点
                .filter(node -> isValidNode(node))
                // 处理规格型号拆分和items填充
                .peek(node -> {
                    // 拆分规格型号并填充到items
                    splitModelAndFillItems(node);
                    // 初始化子节点列表(避免空指针)
                    if (node.getChildren() == null) {
                        node.setChildren(new ArrayList<>());
                    }
                })
                .collect(Collectors.toList());
    }
    /**
     * 验证节点是否有效(核心字段校验)
     */
    private static boolean isValidNode(ProductModelExcelDto node) {
        if (node == null) {
            return false;
        }
        // 序号必须有效(基础校验)
        if (!isValidSequence(node.getNumber())) {
            return false;
        }
        // 产品名称非空(业务校验,可根据实际需求调整)
        return StringUtils.isNotBlank(node.getProductName());
    }
    /**
     * 规格型号拆分并填充到items集合
     * @param node 待处理的节点
     */
    private static void splitModelAndFillItems(ProductModelExcelDto node) {
        // 1. 获取原始规格型号
        String originalModel = node.getModel();
        List<ProductModelExcelItemDto> items = new ArrayList<>();
        if (StringUtils.isNotBlank(originalModel)) {
            // 2. 按"+"拆分规格型号(如果有多个),处理每个片段
            List<String> splitModels = Arrays.stream(originalModel.split("\\+"))
                    .map(String::trim)
                    .filter(StringUtils::isNotBlank)
                    .distinct()
                    .collect(Collectors.toList());
            // 3. 为每个规格创建Item(即使只有一个,也生成一个Item)
            items = splitModels.stream()
                    .map(model -> createExcelItem(node, model))
                    .collect(Collectors.toList());
        }
        // 4. 填充到节点的items集合(覆盖原有数据)
        node.setItems(items);
    }
    /**
     * 创建ProductModelExcelItemDto对象(继承原节点的公共属性)
     * @param node 原节点
     * @param model 拆分后的规格型号
     * @return 构建好的Item对象
     */
    private static ProductModelExcelItemDto createExcelItem(ProductModelExcelDto node, String model) {
        ProductModelExcelItemDto item = new ProductModelExcelItemDto();
        // 1. 拆分后的规格型号
        item.setModel(model);
        // 2. 继承原节点的单位(空值处理)
        item.setUnit(StringUtils.defaultIfBlank(node.getUnit(), ""));
        // 3. 继承原节点的产品类型(空值处理,默认0表示未知)
        item.setProductType(Optional.ofNullable(node.getProductType()).orElse(0));
        // 4. 继承原节点的图纸编号(空值处理)
        item.setDrawingNumber(StringUtils.defaultIfBlank(node.getDrawingNumber(), ""));
        return item;
    }
    /**
     * 验证序号格式是否有效(支持1、1.1、1.1.1等格式)
     */
    private static boolean isValidSequence(String sequence) {
        if (StringUtils.isBlank(sequence)) {
            return false;
        }
        // 正则表达式:匹配数字开头,后续可跟.和数字
        return sequence.trim().matches("^\\d+(\\.\\d+)*$");
    }
    /**
     * 按节点层级分组
     */
    private static Map<Integer, List<ProductModelExcelDto>> groupByLevel(List<ProductModelExcelDto> validList) {
        return validList.stream()
                .collect(Collectors.groupingBy(
                        node -> getNodeLevel(node.getNumber()),  // 计算层级
                        TreeMap::new,  // 按层级升序排序
                        Collectors.toList()
                ));
    }
    /**
     * 计算节点层级(序号中.的数量 + 1)
     * 例:1 -> 1级,1.1 -> 2级,1.1.1 -> 3级
     */
    private static int getNodeLevel(String sequence) {
        if (StringUtils.isBlank(sequence)) {
            return 0;
        }
        String trimmedSeq = sequence.trim();
        return trimmedSeq.split("\\.").length;
    }
    /**
     * 构建节点映射表(序号 -> 节点)
     */
    private static Map<String, ProductModelExcelDto> buildNodeMap(List<ProductModelExcelDto> validList) {
        return validList.stream()
                .collect(Collectors.toMap(
                        node -> node.getNumber().trim(),  // 键:序号(去空格)
                        node -> node,                     // 值:节点对象
                        (oldVal, newVal) -> oldVal        // 处理重复序号:保留第一个
                ));
    }
    /**
     * 构建父子节点关系
     */
    private static void buildParentChildRelation(Map<Integer, List<ProductModelExcelDto>> levelGroupMap,
                                                 Map<String, ProductModelExcelDto> nodeMap) {
        // 从层级2开始处理(层级1是根节点,无父节点)
        for (Map.Entry<Integer, List<ProductModelExcelDto>> entry : levelGroupMap.entrySet()) {
            int currentLevel = entry.getKey();
            if (currentLevel <= 1) {
                continue;  // 跳过根节点层级
            }
            List<ProductModelExcelDto> currentLevelNodes = entry.getValue();
            for (ProductModelExcelDto currentNode : currentLevelNodes) {
                // 计算父节点序号
                String parentSequence = getParentSequence(currentNode.getNumber());
                if (StringUtils.isBlank(parentSequence)) {
                    continue;
                }
                // 查找父节点
                ProductModelExcelDto parentNode = nodeMap.get(parentSequence);
                if (parentNode != null && parentNode.getChildren() != null) {
                    // 添加到父节点的子节点列表
                    parentNode.getChildren().add(currentNode);
                }
            }
        }
    }
    /**
     * 根据当前节点序号获取父节点序号
     * 例:1.1.2 -> 1.1,1.2 -> 1,1 -> null
     */
    private static String getParentSequence(String currentSequence) {
        if (StringUtils.isBlank(currentSequence)) {
            return null;
        }
        String trimmedSeq = currentSequence.trim();
        String[] seqParts = trimmedSeq.split("\\.");
        if (seqParts.length <= 1) {
            return null;  // 根节点无父节点
        }
        // 去掉最后一部分,拼接父节点序号
        String[] parentParts = Arrays.copyOfRange(seqParts, 0, seqParts.length - 1);
        return String.join(".", parentParts);
    }
    /**
     * 获取根节点列表(层级1的节点)
     */
    private static List<ProductModelExcelDto> getRootNodes(Map<Integer, List<ProductModelExcelDto>> levelGroupMap) {
        return levelGroupMap.getOrDefault(1, new ArrayList<>());
    }
    /**
     * 递归排序所有节点(按序号数字顺序)
     */
    private static void sortTreeNodes(List<ProductModelExcelDto> nodeList) {
        if (CollectionUtils.isEmpty(nodeList)) {
            return;
        }
        // 排序当前层级节点
        nodeList.sort(Comparator.comparing(
                ProductModelExcelDto::getNumber,
                new SequenceComparator()  // 自定义序号比较器
        ));
        // 递归排序子节点
        for (ProductModelExcelDto node : nodeList) {
            sortTreeNodes(node.getChildren());
        }
    }
    /**
     * 序号比较器:按数字顺序排序(支持1.10在1.2之后)
     */
    private static class SequenceComparator implements Comparator<String> {
        @Override
        public int compare(String seq1, String seq2) {
            if (StringUtils.isBlank(seq1) && StringUtils.isBlank(seq2)) {
                return 0;
            }
            if (StringUtils.isBlank(seq1)) {
                return -1;
            }
            if (StringUtils.isBlank(seq2)) {
                return 1;
            }
            // 分割序号为数字数组
            String[] parts1 = seq1.trim().split("\\.");
            String[] parts2 = seq2.trim().split("\\.");
            // 逐段比较数字
            int minLength = Math.min(parts1.length, parts2.length);
            for (int i = 0; i < minLength; i++) {
                int num1 = parseSequencePart(parts1[i]);
                int num2 = parseSequencePart(parts2[i]);
                if (num1 != num2) {
                    return num1 - num2;
                }
            }
            // 长度不同时,短的在前(如1.1在1.1.1之前)
            return parts1.length - parts2.length;
        }
        /**
         * 解析序号片段为数字(处理异常情况)
         */
        private int parseSequencePart(String part) {
            try {
                return Integer.parseInt(part.trim());
            } catch (NumberFormatException e) {
                return 0;  // 异常情况按0处理
            }
        }
    }
    /**
     * 产品类型映射(1=物料,2=产品,0=未知)
     */
    private static String mapProductType(Integer productType) {
        return Optional.ofNullable(productType)
                .map(type -> type == 1 ? "物料" : (type == 2 ? "产品" : "未知"))
                .orElse("未知");
    }
}