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("未知");
|
}
|
|
|
|
|
}
|