3 天以前 f4d4d29368ccacb807f93e2033cd4a643a3ddade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
package com.ruoyi.common.utils;
 
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;
 
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * @author :yys
 * @date : 2025/9/15 15:31
 */
public class OrderUtils {
 
 
    /**
     * 查询当天(基于createTime字段)的记录数量
     * @param mapper 实体类对应的BaseMapper
     * @param <T> 实体类泛型
     * @return 当天记录数量
     */
    public static <T> String countTodayByCreateTime(BaseMapper<T> mapper,String preFix) {
        // 获取当天开始时间(00:00:00)
        LocalDateTime todayStart = LocalDateTime.of(
                LocalDateTime.now().toLocalDate(),
                LocalTime.MIN
        );
        // 获取当天结束时间(23:59:59.999)
        LocalDateTime todayEnd = LocalDateTime.of(
                LocalDateTime.now().toLocalDate(),
                LocalTime.MAX
        );
 
        // 转换为Date类型(如果实体类中createTime是LocalDateTime可直接使用)
        Date startDate = Date.from(todayStart.atZone(ZoneId.systemDefault()).toInstant());
        Date endDate = Date.from(todayEnd.atZone(ZoneId.systemDefault()).toInstant());
 
        // 构建查询条件
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        queryWrapper.ge("create_time", startDate)  // 大于等于当天开始
                .lt("create_time", endDate);   // 小于当天结束(避免毫秒精度问题)
 
        // 执行查询
        Long aLong = mapper.selectCount(queryWrapper);
        // 拼接订单编号 preFix + 时间(yyyyMMdd) + 订单数量(001)
        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("未知");
    }
 
 
 
 
}