|
|
@@ -0,0 +1,1416 @@
|
|
|
+package shop.alien.store.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.apache.poi.ss.usermodel.*;
|
|
|
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+import shop.alien.entity.result.R;
|
|
|
+import shop.alien.entity.store.StoreDictionary;
|
|
|
+import shop.alien.entity.store.excelVo.DictionaryLibraryExcelVo;
|
|
|
+import shop.alien.entity.store.excelVo.util.ExcelHeader;
|
|
|
+import shop.alien.mapper.StoreDictionaryMapper;
|
|
|
+import shop.alien.store.service.DictionaryLibraryService;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.OutputStream;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Date;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 平台字典库管理ServiceImpl
|
|
|
+ *
|
|
|
+ * @author ssk
|
|
|
+ * @version 1.0
|
|
|
+ * @date 2025/01/01
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class DictionaryLibraryServiceImpl extends ServiceImpl<StoreDictionaryMapper, StoreDictionary> implements DictionaryLibraryService {
|
|
|
+
|
|
|
+ private final StoreDictionaryMapper storeDictionaryMapper;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询字典库(三级树形结构)
|
|
|
+ * 如果传入dictDetail,则查询匹配的记录及其父级和子级树形结构
|
|
|
+ * 如果不传dictDetail,则返回完整的字典库树形结构
|
|
|
+ *
|
|
|
+ * @param dictDetail 字典描述(可选),如果传入则根据此参数过滤
|
|
|
+ * @return 字典库树形结构列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<StoreDictionary> queryDictionaryLibraryTree(String dictDetail) {
|
|
|
+ // 如果传入了dictDetail,使用过滤逻辑
|
|
|
+ if (StringUtils.isNotBlank(dictDetail)) {
|
|
|
+ return selectDictBydictDetail(dictDetail);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 否则使用原有逻辑:查询所有字典库数据
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.orderByAsc(StoreDictionary::getSortId);
|
|
|
+ List<StoreDictionary> storeDictionaryList = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ // 构建三级树形结构
|
|
|
+ return buildTreeOptimized(storeDictionaryList);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建树形结构(优化版)
|
|
|
+ *
|
|
|
+ * @param flatList 扁平列表
|
|
|
+ * @return 树形结构列表
|
|
|
+ */
|
|
|
+ private List<StoreDictionary> buildTreeOptimized(List<StoreDictionary> flatList) {
|
|
|
+ if (flatList == null || flatList.isEmpty()) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建三个存储结构
|
|
|
+ Map<Integer, StoreDictionary> nodeMap = new HashMap<>(); // ID到节点的映射
|
|
|
+ Map<Integer, List<StoreDictionary>> parentChildMap = new HashMap<>(); // 父ID到子节点列表的映射
|
|
|
+ List<StoreDictionary> result = new ArrayList<>(); // 结果列表
|
|
|
+
|
|
|
+ // 填充nodeMap和parentChildMap
|
|
|
+ for (StoreDictionary entity : flatList) {
|
|
|
+ Integer id = entity.getId();
|
|
|
+ Integer parentId = entity.getParentId();
|
|
|
+
|
|
|
+ // 存入节点映射
|
|
|
+ nodeMap.put(id, entity);
|
|
|
+
|
|
|
+ // 初始化子节点列表
|
|
|
+ entity.setStoreDictionaryList(new ArrayList<>());
|
|
|
+
|
|
|
+ // 如果是根节点(parentId为null或0),直接添加到结果
|
|
|
+ if (parentId == null || parentId == 0) {
|
|
|
+ result.add(entity);
|
|
|
+ } else {
|
|
|
+ // 否则,记录父子关系
|
|
|
+ parentChildMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(entity);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 建立父子关系
|
|
|
+ for (StoreDictionary entity : flatList) {
|
|
|
+ Integer id = entity.getId();
|
|
|
+ if (parentChildMap.containsKey(id)) {
|
|
|
+ entity.getStoreDictionaryList().addAll(parentChildMap.get(id));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增字典库(支持一级、二级、三级)
|
|
|
+ *
|
|
|
+ * @param storeDictionary 字典库信息
|
|
|
+ * @return 新增结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean addDictionaryLibrary(StoreDictionary storeDictionary) {
|
|
|
+ // 参数校验
|
|
|
+ if (storeDictionary == null || StringUtils.isBlank(storeDictionary.getDictDetail())) {
|
|
|
+ throw new IllegalArgumentException("字典库描述不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置固定字段
|
|
|
+// storeDictionary.setTypeName("dictionary_library");
|
|
|
+// if (StringUtils.isBlank(storeDictionary.getTypeDetail())) {
|
|
|
+// storeDictionary.setTypeDetail("字典库");
|
|
|
+// }
|
|
|
+ storeDictionary.setDeleteFlag(0);
|
|
|
+ storeDictionary.setCreatedTime(new Date());
|
|
|
+
|
|
|
+ // 处理 parent_id:如果为 null,设置为 0(一级)
|
|
|
+ Integer parentId = storeDictionary.getParentId();
|
|
|
+ if (parentId == null) {
|
|
|
+ parentId = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果是二级或三级,验证父节点是否存在
|
|
|
+ if (parentId != 0) {
|
|
|
+ StoreDictionary parent = storeDictionaryMapper.selectById(parentId);
|
|
|
+ if (parent == null || parent.getDeleteFlag() == 1) {
|
|
|
+ throw new IllegalArgumentException("父节点不存在或已删除");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成 dict_id:根据 typeName 查询最大 dict_id,然后 +1
|
|
|
+ String typeName = storeDictionary.getTypeName();
|
|
|
+ if (StringUtils.isBlank(typeName)) {
|
|
|
+ throw new IllegalArgumentException("typeName不能为空");
|
|
|
+ }
|
|
|
+ String maxDictId = getMaxDictIdByTypeName(typeName);
|
|
|
+ int nextDictId = (maxDictId == null ? 1 : Integer.parseInt(maxDictId)) + 1;
|
|
|
+ storeDictionary.setDictId(String.valueOf(nextDictId));
|
|
|
+
|
|
|
+ // 生成 sort_id
|
|
|
+ Integer maxSortId = getMaxSortIdByParentId(parentId);
|
|
|
+ storeDictionary.setSortId(maxSortId == null ? 1 : maxSortId + 1);
|
|
|
+
|
|
|
+ // 保存
|
|
|
+ return this.save(storeDictionary);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改字典库(支持一级、二级、三级)
|
|
|
+ *
|
|
|
+ * @param storeDictionary 字典库信息
|
|
|
+ * @return 修改结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public boolean updateDictionaryLibrary(StoreDictionary storeDictionary) {
|
|
|
+ // 参数校验
|
|
|
+ if (storeDictionary == null || storeDictionary.getId() == null) {
|
|
|
+ throw new IllegalArgumentException("ID不能为空");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(storeDictionary.getDictDetail())) {
|
|
|
+ throw new IllegalArgumentException("字典库描述不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询原记录
|
|
|
+ StoreDictionary existing = storeDictionaryMapper.selectById(storeDictionary.getId());
|
|
|
+ if (existing == null) {
|
|
|
+ throw new IllegalArgumentException("记录不存在");
|
|
|
+ }
|
|
|
+ if (existing.getDeleteFlag() == 1) {
|
|
|
+ throw new IllegalArgumentException("该记录已删除");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 判断是否是一级或二级节点
|
|
|
+ Integer existingParentId = existing.getParentId();
|
|
|
+ boolean isFirstLevel = (existingParentId == null || existingParentId == 0);
|
|
|
+ boolean isSecondLevel = false;
|
|
|
+
|
|
|
+ // 如果不是一级节点,判断是否是二级节点(父节点是一级节点)
|
|
|
+ if (!isFirstLevel) {
|
|
|
+ StoreDictionary parent = storeDictionaryMapper.selectById(existingParentId);
|
|
|
+ if (parent != null) {
|
|
|
+ Integer grandParentId = parent.getParentId();
|
|
|
+ isSecondLevel = (grandParentId == null || grandParentId == 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理 parent_id:如果为 null,设置为 0(一级)
|
|
|
+ Integer parentId = storeDictionary.getParentId();
|
|
|
+ if (parentId == null) {
|
|
|
+ parentId = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 标准化parentId用于比较(null转为0)
|
|
|
+ Integer normalizedParentId = (parentId == null) ? 0 : parentId;
|
|
|
+ Integer normalizedExistingParentId = (existingParentId == null) ? 0 : existingParentId;
|
|
|
+ Integer oldSortId = existing.getSortId();
|
|
|
+ Integer newSortId = storeDictionary.getSortId();
|
|
|
+ boolean parentIdChanged = !normalizedParentId.equals(normalizedExistingParentId);
|
|
|
+
|
|
|
+ // 判断是否修改了关键字段(parent_id)
|
|
|
+ boolean keyFieldChanged = parentIdChanged;
|
|
|
+
|
|
|
+ // 如果是一级或二级节点,且修改了关键字段(parent_id),检查是否有未删除的子节点
|
|
|
+ if ((isFirstLevel || isSecondLevel) && keyFieldChanged) {
|
|
|
+ boolean hasChildren = hasUndeletedChildren(existing.getId());
|
|
|
+ if (hasChildren) {
|
|
|
+ throw new IllegalArgumentException("该节点存在未删除的子节点,不允许修改关键字段(父节点)");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果修改了 parentId,需要验证新的父节点是否存在
|
|
|
+ if (parentIdChanged) {
|
|
|
+ if (parentId != 0) {
|
|
|
+ StoreDictionary parent = storeDictionaryMapper.selectById(parentId);
|
|
|
+ if (parent == null || parent.getDeleteFlag() == 1) {
|
|
|
+ throw new IllegalArgumentException("父节点不存在或已删除");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果用户指定了新位置(newSortId不为空),使用用户指定的位置
|
|
|
+ // 否则,使用新节点下的最大+1
|
|
|
+ if (newSortId == null) {
|
|
|
+ Integer maxSortId = getMaxSortIdByParentId(parentId);
|
|
|
+ int nextSortId = (maxSortId == null ? 1 : maxSortId) + 1;
|
|
|
+ newSortId = nextSortId;
|
|
|
+ storeDictionary.setSortId(newSortId);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原节点下,原位置之后的记录需要前移(dictId - 1)
|
|
|
+ adjustSortOrderForMoveOut(existing.getId(), normalizedExistingParentId, oldSortId);
|
|
|
+
|
|
|
+ // 新节点下,如果指定了新位置,需要调整新节点下的排序
|
|
|
+ if (newSortId != null) {
|
|
|
+ try {
|
|
|
+ int newOrder = newSortId;
|
|
|
+ Integer maxSortId = getMaxSortIdByParentId(parentId);
|
|
|
+ int maxOrder = (maxSortId == null ? 0 : maxSortId);
|
|
|
+ // 如果新位置不是最大+1,需要调整新节点下的排序
|
|
|
+ if (newOrder <= maxOrder) {
|
|
|
+ adjustSortOrderForMoveIn(existing.getId(), normalizedParentId, newSortId);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("无法解析新sortId: {}", newSortId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置固定字段
|
|
|
+// storeDictionary.setTypeName("dictionary_library");
|
|
|
+ // 如果没有指定删除标记,保持原有值;如果指定了,使用新值
|
|
|
+ Integer oldHidden = existing.getHidden();
|
|
|
+ Integer newHidden = storeDictionary.getHidden() == null ? oldHidden : storeDictionary.getHidden();
|
|
|
+ storeDictionary.setHidden(newHidden);
|
|
|
+ storeDictionary.setDeleteFlag(existing.getDeleteFlag()); // 保持原有删除标记
|
|
|
+ storeDictionary.setCreatedTime(existing.getCreatedTime()); // 保持原有创建时间
|
|
|
+ storeDictionary.setCreatedUserId(existing.getCreatedUserId()); // 保持原有创建人
|
|
|
+ storeDictionary.setUpdatedTime(new Date()); // 更新修改时间
|
|
|
+
|
|
|
+ // 处理排序逻辑:如果 parentId 没有改变,但 dictId 改变了,需要调整同级其他记录的排序
|
|
|
+ if (!parentIdChanged) {
|
|
|
+ // 如果 dictId 为空,保持原有值
|
|
|
+ if (newSortId == null) {
|
|
|
+ storeDictionary.setSortId(oldSortId);
|
|
|
+ } else if (!newSortId.equals(oldSortId)) {
|
|
|
+ // 调整同级其他记录的排序
|
|
|
+ adjustSortOrder(existing.getId(), normalizedParentId, oldSortId, newSortId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新当前节点
|
|
|
+ boolean updateResult = this.updateById(storeDictionary);
|
|
|
+
|
|
|
+ // 如果是一级或二级节点,且hidden值发生了变化,同步更新所有子节点的hidden值
|
|
|
+ boolean hiddenChanged = (oldHidden == null && newHidden != null) ||
|
|
|
+ (oldHidden != null && !oldHidden.equals(newHidden));
|
|
|
+ if (updateResult && (isFirstLevel || isSecondLevel) && hiddenChanged && newHidden != null) {
|
|
|
+ updateChildrenHidden(existing.getId(), newHidden);
|
|
|
+ }
|
|
|
+
|
|
|
+ return updateResult;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断节点是否有未删除的子节点
|
|
|
+ *
|
|
|
+ * @param parentId 父节点ID
|
|
|
+ * @return 如果有未删除的子节点返回 true,否则返回 false
|
|
|
+ */
|
|
|
+ private boolean hasUndeletedChildren(Integer parentId) {
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, parentId);
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.last("LIMIT 1");
|
|
|
+
|
|
|
+ StoreDictionary child = storeDictionaryMapper.selectOne(queryWrapper);
|
|
|
+ return child != null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归更新子节点的显示/隐藏状态
|
|
|
+ * 当一级或二级节点的hidden值改变时,同步更新其所有子节点的hidden值
|
|
|
+ *
|
|
|
+ * @param parentId 父节点ID
|
|
|
+ * @param hidden 新的hidden值(0:不隐藏, 1:隐藏)
|
|
|
+ */
|
|
|
+ private void updateChildrenHidden(Integer parentId, Integer hidden) {
|
|
|
+ if (parentId == null || hidden == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询所有未删除的子节点
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, parentId);
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+
|
|
|
+ List<StoreDictionary> children = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (children == null || children.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量更新子节点的hidden值
|
|
|
+ Date updateTime = new Date();
|
|
|
+ for (StoreDictionary child : children) {
|
|
|
+ child.setHidden(hidden);
|
|
|
+ child.setUpdatedTime(updateTime);
|
|
|
+ storeDictionaryMapper.updateById(child);
|
|
|
+
|
|
|
+ // 递归更新子节点的子节点(二级节点的子节点是三级节点)
|
|
|
+ updateChildrenHidden(child.getId(), hidden);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据 typeName 获取最大 dict_id
|
|
|
+ *
|
|
|
+ * @param typeName 类型名称
|
|
|
+ * @return 最大 dict_id,如果不存在则返回 null
|
|
|
+ */
|
|
|
+ private String getMaxDictIdByTypeName(String typeName) {
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.eq(StoreDictionary::getTypeName, typeName);
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+
|
|
|
+ queryWrapper.orderByDesc(StoreDictionary::getDictId);
|
|
|
+ queryWrapper.last("LIMIT 1");
|
|
|
+
|
|
|
+ StoreDictionary maxDict = storeDictionaryMapper.selectOne(queryWrapper);
|
|
|
+ return maxDict != null ? maxDict.getDictId() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据parentId获取最大的sortId
|
|
|
+ *
|
|
|
+ * @param parentId 父节点ID
|
|
|
+ * @return 最大的sortId,如果不存在则返回null
|
|
|
+ */
|
|
|
+ private Integer getMaxSortIdByParentId(Integer parentId) {
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+
|
|
|
+ if (parentId == null || parentId == 0) {
|
|
|
+ queryWrapper.and(wrapper -> wrapper.isNull(StoreDictionary::getParentId)
|
|
|
+ .or().eq(StoreDictionary::getParentId, 0));
|
|
|
+ } else {
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, parentId);
|
|
|
+ }
|
|
|
+
|
|
|
+ queryWrapper.orderByDesc(StoreDictionary::getSortId);
|
|
|
+ queryWrapper.last("LIMIT 1");
|
|
|
+
|
|
|
+ StoreDictionary maxDict = storeDictionaryMapper.selectOne(queryWrapper);
|
|
|
+ return maxDict != null && maxDict.getSortId() != null ? maxDict.getSortId() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 调整排序:当记录的dictId改变时,调整同级其他记录的排序
|
|
|
+ * 上升则顺推:如果新dictId < 原dictId,将原dictId到新dictId之间的记录的dictId都+1
|
|
|
+ * 下降则顺升:如果新dictId > 原dictId,将原dictId到新dictId之间的记录的dictId都-1
|
|
|
+ *
|
|
|
+ * @param currentId 当前记录ID
|
|
|
+ * @param parentId 父节点ID
|
|
|
+ * @param oldSortId 原sortId
|
|
|
+ * @param newSortId 新sortId
|
|
|
+ */
|
|
|
+ private void adjustSortOrder(Integer currentId, Integer parentId, Integer oldSortId, Integer newSortId) {
|
|
|
+ try {
|
|
|
+ int oldOrder = oldSortId;
|
|
|
+ int newOrder = newSortId;
|
|
|
+
|
|
|
+ // 如果排序没有改变,直接返回
|
|
|
+ if (oldOrder == newOrder) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询同级所有记录(排除当前记录)
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.ne(StoreDictionary::getId, currentId);
|
|
|
+
|
|
|
+ if (parentId == null || parentId == 0) {
|
|
|
+ queryWrapper.and(wrapper -> wrapper.isNull(StoreDictionary::getParentId)
|
|
|
+ .or().eq(StoreDictionary::getParentId, 0));
|
|
|
+ } else {
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, parentId);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreDictionary> siblings = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (siblings == null || siblings.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 上升则顺推:新dictId < 原dictId
|
|
|
+ if (newOrder < oldOrder) {
|
|
|
+ // 将新dictId到原dictId之间的记录的dictId都+1
|
|
|
+ for (StoreDictionary sibling : siblings) {
|
|
|
+ try {
|
|
|
+ int siblingOrder = sibling.getSortId();
|
|
|
+ if (siblingOrder >= newOrder && siblingOrder < oldOrder) {
|
|
|
+ sibling.setSortId(siblingOrder + 1);
|
|
|
+ sibling.setUpdatedTime(new Date());
|
|
|
+ storeDictionaryMapper.updateById(sibling);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("无法解析sortId: {}", sibling.getSortId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 下降则顺升:新dictId > 原dictId
|
|
|
+ else if (newOrder > oldOrder) {
|
|
|
+ // 将原dictId到新dictId之间的记录的dictId都-1
|
|
|
+ for (StoreDictionary sibling : siblings) {
|
|
|
+ try {
|
|
|
+ int siblingOrder = sibling.getSortId();
|
|
|
+ if (siblingOrder > oldOrder && siblingOrder <= newOrder) {
|
|
|
+ sibling.setSortId(siblingOrder - 1);
|
|
|
+ sibling.setUpdatedTime(new Date());
|
|
|
+ storeDictionaryMapper.updateById(sibling);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("无法解析sortId: {}", sibling.getSortId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.error("调整排序失败,sortId格式错误: oldSortId={}, newSortId={}", oldSortId, newSortId, e);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("调整排序失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理从原节点移出时的排序调整:原位置之后的记录需要前移(dictId - 1)
|
|
|
+ *
|
|
|
+ * @param currentId 当前记录ID
|
|
|
+ * @param oldParentId 原父节点ID
|
|
|
+ * @param oldSortId 原sortId
|
|
|
+ */
|
|
|
+ private void adjustSortOrderForMoveOut(Integer currentId, Integer oldParentId, Integer oldSortId) {
|
|
|
+ try {
|
|
|
+ int oldOrder = oldSortId;
|
|
|
+
|
|
|
+ // 查询原节点同级所有记录(排除当前记录)
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.ne(StoreDictionary::getId, currentId);
|
|
|
+
|
|
|
+ if (oldParentId == null || oldParentId == 0) {
|
|
|
+ queryWrapper.and(wrapper -> wrapper.isNull(StoreDictionary::getParentId)
|
|
|
+ .or().eq(StoreDictionary::getParentId, 0));
|
|
|
+ } else {
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, oldParentId);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreDictionary> siblings = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (siblings == null || siblings.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 原位置之后的记录需要前移(dictId - 1)
|
|
|
+ for (StoreDictionary sibling : siblings) {
|
|
|
+ try {
|
|
|
+ int siblingOrder = sibling.getSortId();
|
|
|
+ if (siblingOrder > oldOrder) {
|
|
|
+ sibling.setSortId(siblingOrder - 1);
|
|
|
+ sibling.setUpdatedTime(new Date());
|
|
|
+ storeDictionaryMapper.updateById(sibling);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("无法解析sortId: {}", sibling.getSortId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.error("移出节点排序调整失败,sortId格式错误: oldSortId={}", oldSortId, e);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("移出节点排序调整失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理移入新节点时的排序调整:新位置之后的记录需要后移(dictId + 1)
|
|
|
+ *
|
|
|
+ * @param currentId 当前记录ID
|
|
|
+ * @param newParentId 新父节点ID
|
|
|
+ * @param newSortId 新sortId
|
|
|
+ */
|
|
|
+ private void adjustSortOrderForMoveIn(Integer currentId, Integer newParentId, Integer newSortId) {
|
|
|
+ try {
|
|
|
+ int newOrder = newSortId;
|
|
|
+
|
|
|
+ // 查询新节点同级所有记录(排除当前记录)
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.ne(StoreDictionary::getId, currentId);
|
|
|
+
|
|
|
+ if (newParentId == null || newParentId == 0) {
|
|
|
+ queryWrapper.and(wrapper -> wrapper.isNull(StoreDictionary::getParentId)
|
|
|
+ .or().eq(StoreDictionary::getParentId, 0));
|
|
|
+ } else {
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, newParentId);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreDictionary> siblings = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (siblings == null || siblings.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 新位置之后的记录需要后移(dictId + 1)
|
|
|
+ for (StoreDictionary sibling : siblings) {
|
|
|
+ try {
|
|
|
+ int siblingOrder = sibling.getSortId();
|
|
|
+ if (siblingOrder >= newOrder) {
|
|
|
+ sibling.setSortId(siblingOrder + 1);
|
|
|
+ sibling.setUpdatedTime(new Date());
|
|
|
+ storeDictionaryMapper.updateById(sibling);
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("无法解析dictId: {}", sibling.getDictId(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.error("移入节点排序调整失败,sortId格式错误: newSortId={}", newSortId, e);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("移入节点排序调整失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量导入字典库
|
|
|
+ *
|
|
|
+ * @param file Excel文件
|
|
|
+ * @return 导入结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R<String> importDictionaryLibrary(MultipartFile file) {
|
|
|
+ log.info("DictionaryLibraryServiceImpl.importDictionaryLibrary fileName={}",
|
|
|
+ file != null ? file.getOriginalFilename() : "null");
|
|
|
+ try {
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ return R.fail("文件不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ String fileName = file.getOriginalFilename();
|
|
|
+ if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
|
|
|
+ return R.fail("文件格式不正确,请上传Excel文件");
|
|
|
+ }
|
|
|
+
|
|
|
+ List<String> errorMessages = new ArrayList<>();
|
|
|
+ int successCount = 0;
|
|
|
+ int totalCount = 0;
|
|
|
+
|
|
|
+ // 使用POI读取Excel
|
|
|
+ try (InputStream inputStream = file.getInputStream();
|
|
|
+ Workbook workbook = new XSSFWorkbook(inputStream)) {
|
|
|
+ Sheet sheet = workbook.getSheetAt(0);
|
|
|
+
|
|
|
+ // 获取表头
|
|
|
+ Row headerRow = sheet.getRow(5);
|
|
|
+ if (headerRow == null) {
|
|
|
+ return R.fail("Excel文件格式不正确,缺少表头");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建字段映射(表头名称 -> 列索引)
|
|
|
+ Map<String, Integer> headerMap = new HashMap<>();
|
|
|
+ Field[] fields = DictionaryLibraryExcelVo.class.getDeclaredFields();
|
|
|
+ for (int i = 0; i < headerRow.getLastCellNum(); i++) {
|
|
|
+ Cell cell = headerRow.getCell(i);
|
|
|
+ if (cell != null) {
|
|
|
+ String headerName = getCellValueAsString(cell);
|
|
|
+ if (StringUtils.isNotBlank(headerName)) {
|
|
|
+ headerMap.put(headerName.trim(), i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 读取数据行
|
|
|
+ for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
|
|
|
+ Row row = sheet.getRow(rowIndex);
|
|
|
+ if (row == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否为空行
|
|
|
+ boolean isEmptyRow = true;
|
|
|
+ for (int i = 0; i < row.getLastCellNum(); i++) {
|
|
|
+ Cell cell = row.getCell(i);
|
|
|
+ if (cell != null && cell.getCellType() != CellType.BLANK) {
|
|
|
+ String cellValue = getCellValueAsString(cell);
|
|
|
+ if (StringUtils.isNotBlank(cellValue)) {
|
|
|
+ isEmptyRow = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (isEmptyRow) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ totalCount++;
|
|
|
+ DictionaryLibraryExcelVo excelVo = new DictionaryLibraryExcelVo();
|
|
|
+
|
|
|
+ // 读取每个字段
|
|
|
+ for (Field field : fields) {
|
|
|
+ if (!field.isAnnotationPresent(ExcelHeader.class)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ ExcelHeader excelHeader = field.getAnnotation(ExcelHeader.class);
|
|
|
+ String headerName = excelHeader.value();
|
|
|
+ Integer colIndex = headerMap.get(headerName);
|
|
|
+ if (colIndex == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ Cell cell = row.getCell(colIndex);
|
|
|
+ if (cell == null) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ field.setAccessible(true);
|
|
|
+ try {
|
|
|
+ String cellValue = getCellValueAsString(cell);
|
|
|
+ if (StringUtils.isNotBlank(cellValue)) {
|
|
|
+ field.set(excelVo, cellValue.trim());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("读取字段{}失败:{}", headerName, e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理导入数据
|
|
|
+ try {
|
|
|
+ processImportData(excelVo, rowIndex + 1, errorMessages);
|
|
|
+ successCount++;
|
|
|
+ } catch (Exception e) {
|
|
|
+ errorMessages.add(String.format("第%d行:%s", rowIndex + 1, e.getMessage()));
|
|
|
+ log.error("导入第{}行数据失败", rowIndex + 1, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建返回消息
|
|
|
+ StringBuilder message = new StringBuilder();
|
|
|
+ message.append(String.format("导入完成:成功%d条,失败%d条", successCount, totalCount - successCount));
|
|
|
+ if (!errorMessages.isEmpty()) {
|
|
|
+ message.append("\n失败详情:\n");
|
|
|
+ int maxErrors = Math.min(errorMessages.size(), 10); // 最多显示10条错误
|
|
|
+ for (int i = 0; i < maxErrors; i++) {
|
|
|
+ message.append(errorMessages.get(i)).append("\n");
|
|
|
+ }
|
|
|
+ if (errorMessages.size() > 10) {
|
|
|
+ message.append(String.format("...还有%d条错误未显示", errorMessages.size() - 10));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (successCount == 0) {
|
|
|
+ return R.fail(message.toString());
|
|
|
+ } else if (totalCount - successCount > 0) {
|
|
|
+ return R.success(message.toString());
|
|
|
+ } else {
|
|
|
+ return R.success(message.toString());
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导入字典库失败", e);
|
|
|
+ return R.fail("导入失败:" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理导入数据
|
|
|
+ *
|
|
|
+ * @param excelVo Excel数据对象
|
|
|
+ * @param rowIndex 行号
|
|
|
+ * @param errorMessages 错误消息列表
|
|
|
+ */
|
|
|
+ private void processImportData(DictionaryLibraryExcelVo excelVo, int rowIndex, List<String> errorMessages) {
|
|
|
+ // 校验必填字段
|
|
|
+ if (StringUtils.isBlank(excelVo.getFirstLevelName())) {
|
|
|
+ throw new IllegalArgumentException("一级分类名称不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ String firstLevelName = excelVo.getFirstLevelName().trim();
|
|
|
+ String secondLevelName = StringUtils.isNotBlank(excelVo.getSecondLevelName())
|
|
|
+ ? excelVo.getSecondLevelName().trim() : null;
|
|
|
+ String thirdLevelName = StringUtils.isNotBlank(excelVo.getThirdLevelName())
|
|
|
+ ? excelVo.getThirdLevelName().trim() : null;
|
|
|
+
|
|
|
+ // 确定要创建的级别
|
|
|
+ if (StringUtils.isNotBlank(thirdLevelName)) {
|
|
|
+ // 创建三级分类
|
|
|
+ createOrUpdateLevel(firstLevelName, secondLevelName, thirdLevelName, 3, excelVo.getHidden(), errorMessages, rowIndex);
|
|
|
+ } else if (StringUtils.isNotBlank(secondLevelName)) {
|
|
|
+ // 创建二级分类
|
|
|
+ createOrUpdateLevel(firstLevelName, secondLevelName, null, 2, excelVo.getHidden(), errorMessages, rowIndex);
|
|
|
+ } else {
|
|
|
+ // 创建一级分类
|
|
|
+ createOrUpdateLevel(firstLevelName, null, null, 1, excelVo.getHidden(), errorMessages, rowIndex);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建或更新分类级别
|
|
|
+ *
|
|
|
+ * @param firstLevelName 一级分类名称
|
|
|
+ * @param secondLevelName 二级分类名称
|
|
|
+ * @param thirdLevelName 三级分类名称
|
|
|
+ * @param level 级别(1、2、3)
|
|
|
+ * @param hidden 状态
|
|
|
+ * @param errorMessages 错误消息列表
|
|
|
+ * @param rowIndex 行号
|
|
|
+ */
|
|
|
+ private void createOrUpdateLevel(String firstLevelName, String secondLevelName, String thirdLevelName,
|
|
|
+ int level, String hidden, List<String> errorMessages, int rowIndex) {
|
|
|
+ try {
|
|
|
+ // 查找或创建一级分类
|
|
|
+ StoreDictionary firstLevel = findOrCreateLevel(firstLevelName, null, 0, hidden);
|
|
|
+ if (firstLevel == null) {
|
|
|
+ throw new IllegalArgumentException("创建一级分类失败");
|
|
|
+ }
|
|
|
+ if (level >= 2 && StringUtils.isNotBlank(secondLevelName)) {
|
|
|
+ // 查找或创建二级分类
|
|
|
+ StoreDictionary secondLevel = findOrCreateLevel(secondLevelName, firstLevel.getId(), 1, hidden);
|
|
|
+ if (secondLevel == null) {
|
|
|
+ throw new IllegalArgumentException("创建二级分类失败");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (level >= 3 && StringUtils.isNotBlank(thirdLevelName)) {
|
|
|
+ // 查找或创建三级分类
|
|
|
+ StoreDictionary thirdLevel = findOrCreateLevel(thirdLevelName, secondLevel.getId(), 2, hidden);
|
|
|
+ if (thirdLevel == null) {
|
|
|
+ throw new IllegalArgumentException("创建三级分类失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new IllegalArgumentException(e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查找或创建分类
|
|
|
+ *
|
|
|
+ * @param dictDetail 分类名称
|
|
|
+ * @param parentId 父节点ID
|
|
|
+ * @param expectedLevel 期望的级别(用于校验)
|
|
|
+ * @param hidden 状态
|
|
|
+ * @return 分类对象
|
|
|
+ */
|
|
|
+ private StoreDictionary findOrCreateLevel(String dictDetail, Integer parentId, int expectedLevel, String hidden) {
|
|
|
+ // 查找是否存在
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.eq(StoreDictionary::getDictDetail, dictDetail);
|
|
|
+
|
|
|
+ if (parentId == null || parentId == 0) {
|
|
|
+ queryWrapper.and(wrapper -> wrapper.isNull(StoreDictionary::getParentId)
|
|
|
+ .or().eq(StoreDictionary::getParentId, 0));
|
|
|
+ } else {
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, parentId);
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreDictionary existing = storeDictionaryMapper.selectOne(queryWrapper);
|
|
|
+
|
|
|
+ if (existing != null) {
|
|
|
+ return existing;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建新分类
|
|
|
+ StoreDictionary newDict = new StoreDictionary();
|
|
|
+ newDict.setDictDetail(dictDetail);
|
|
|
+ // 一级为经营版块 ,二级为经营种类,三级为分类
|
|
|
+ if (expectedLevel == 0) {
|
|
|
+ newDict.setTypeDetail("举报");
|
|
|
+ newDict.setTypeName("report");
|
|
|
+ } else if (expectedLevel == 1) {
|
|
|
+ newDict.setTypeDetail("举报种类");
|
|
|
+ newDict.setTypeName("report_type");
|
|
|
+ } else if (expectedLevel == 2) {
|
|
|
+ newDict.setTypeDetail("分类");
|
|
|
+ newDict.setTypeName("report_classify");
|
|
|
+ }
|
|
|
+ newDict.setParentId(parentId == null ? null : parentId);
|
|
|
+ newDict.setHidden(StringUtils.isNotBlank(hidden) && hidden.equals("隐藏") ? 1 : 0);
|
|
|
+ newDict.setDeleteFlag(0);
|
|
|
+ newDict.setCreatedTime(new Date());
|
|
|
+
|
|
|
+ // 生成 dict_id
|
|
|
+ String maxDictId = getMaxDictIdByTypeName("dictionary_library");
|
|
|
+ newDict.setDictId(String.valueOf(maxDictId == null ? 1 : Integer.parseInt(maxDictId) + 1));
|
|
|
+
|
|
|
+ // 生成 sort_id
|
|
|
+ Integer maxSortId = getMaxSortIdByParentId(newDict.getParentId());
|
|
|
+ newDict.setSortId(maxSortId == null ? 1 : maxSortId + 1);
|
|
|
+
|
|
|
+ boolean saved = this.save(newDict);
|
|
|
+ return saved ? newDict : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取单元格值(字符串格式)
|
|
|
+ *
|
|
|
+ * @param cell 单元格
|
|
|
+ * @return 字符串值
|
|
|
+ */
|
|
|
+ private String getCellValueAsString(Cell cell) {
|
|
|
+ if (cell == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (cell.getCellType()) {
|
|
|
+ case STRING:
|
|
|
+ return cell.getStringCellValue();
|
|
|
+ case NUMERIC:
|
|
|
+ if (DateUtil.isCellDateFormatted(cell)) {
|
|
|
+ return cell.getDateCellValue().toString();
|
|
|
+ } else {
|
|
|
+ // 处理数字,避免科学计数法
|
|
|
+ double numericValue = cell.getNumericCellValue();
|
|
|
+ if (numericValue == (long) numericValue) {
|
|
|
+ return String.valueOf((long) numericValue);
|
|
|
+ } else {
|
|
|
+ return String.valueOf(numericValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case BOOLEAN:
|
|
|
+ return String.valueOf(cell.getBooleanCellValue());
|
|
|
+ case FORMULA:
|
|
|
+ return cell.getCellFormula();
|
|
|
+ default:
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 下载字典库导入模板
|
|
|
+ *
|
|
|
+ * @param response HTTP响应
|
|
|
+ * @throws IOException IO异常
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void downloadTemplate(HttpServletResponse response) throws IOException {
|
|
|
+ log.info("DictionaryLibraryServiceImpl.downloadTemplate");
|
|
|
+
|
|
|
+ XSSFWorkbook workbook = new XSSFWorkbook();
|
|
|
+ try {
|
|
|
+ Sheet sheet = workbook.createSheet("字典库导入模板");
|
|
|
+
|
|
|
+ // 获取带有 @ExcelHeader 注解的字段
|
|
|
+ Field[] fields = DictionaryLibraryExcelVo.class.getDeclaredFields();
|
|
|
+ List<Field> excelFields = new ArrayList<>();
|
|
|
+ for (Field field : fields) {
|
|
|
+ if (field.isAnnotationPresent(ExcelHeader.class)) {
|
|
|
+ excelFields.add(field);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建表头样式
|
|
|
+ Font headerFont = workbook.createFont();
|
|
|
+ headerFont.setBold(true);
|
|
|
+ headerFont.setFontHeightInPoints((short) 12);
|
|
|
+ CellStyle headerCellStyle = workbook.createCellStyle();
|
|
|
+ headerCellStyle.setFont(headerFont);
|
|
|
+ headerCellStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ headerCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ headerCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
|
|
+ headerCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
+ headerCellStyle.setBorderBottom(BorderStyle.THIN);
|
|
|
+ headerCellStyle.setBorderTop(BorderStyle.THIN);
|
|
|
+ headerCellStyle.setBorderLeft(BorderStyle.THIN);
|
|
|
+ headerCellStyle.setBorderRight(BorderStyle.THIN);
|
|
|
+
|
|
|
+ // 创建表头
|
|
|
+ Row headerRow = sheet.createRow(0);
|
|
|
+ for (int i = 0; i < excelFields.size(); i++) {
|
|
|
+ Field field = excelFields.get(i);
|
|
|
+ ExcelHeader excelHeader = field.getAnnotation(ExcelHeader.class);
|
|
|
+ Cell cell = headerRow.createCell(i);
|
|
|
+ cell.setCellValue(excelHeader.value());
|
|
|
+ cell.setCellStyle(headerCellStyle);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置列宽
|
|
|
+ sheet.setColumnWidth(0, 25 * 256); // 一级分类名称
|
|
|
+ sheet.setColumnWidth(1, 25 * 256); // 二级分类名称
|
|
|
+ sheet.setColumnWidth(2, 25 * 256); // 三级分类名称
|
|
|
+ sheet.setColumnWidth(3, 15 * 256); // 状态(隐藏/显示)
|
|
|
+
|
|
|
+ // 设置响应头(必须在获取输出流之前设置)
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
+ response.setCharacterEncoding("utf-8");
|
|
|
+ String fileName = URLEncoder.encode("字典库导入模板", "UTF-8").replaceAll("\\+", "%20");
|
|
|
+ response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
|
|
+
|
|
|
+ // 输出到响应流
|
|
|
+ OutputStream outputStream = response.getOutputStream();
|
|
|
+ workbook.write(outputStream);
|
|
|
+ outputStream.flush();
|
|
|
+ } finally {
|
|
|
+ // 确保 workbook 被正确关闭
|
|
|
+ if (workbook != null) {
|
|
|
+ try {
|
|
|
+ workbook.close();
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("关闭workbook失败", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int deleteDictionaryLibrary(Long id) {
|
|
|
+ return storeDictionaryMapper.deleteById(id);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 修改字典库状态(通过id修改hidden为1)
|
|
|
+ *
|
|
|
+ * @param id 字典库ID
|
|
|
+ * @return 修改结果
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public int updateHiddenStatus(Long id) {
|
|
|
+ if (id == null) {
|
|
|
+ throw new IllegalArgumentException("ID不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询原记录
|
|
|
+ StoreDictionary existing = storeDictionaryMapper.selectById(id);
|
|
|
+ if (existing == null) {
|
|
|
+ throw new IllegalArgumentException("记录不存在");
|
|
|
+ }
|
|
|
+ if (existing.getDeleteFlag() == 1) {
|
|
|
+ throw new IllegalArgumentException("该记录已删除");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置hidden为1
|
|
|
+ existing.setHidden(1);
|
|
|
+ existing.setUpdatedTime(new Date());
|
|
|
+
|
|
|
+ // 更新记录
|
|
|
+ return storeDictionaryMapper.updateById(existing);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据dictDetail查询字典库(包含父级或子级树形结构)
|
|
|
+ * 支持查询多个同名数据及其父级和子级
|
|
|
+ *
|
|
|
+ * @param dictDetail 字典描述
|
|
|
+ * @return 字典库树形结构列表
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<StoreDictionary> selectDictBydictDetail(String dictDetail) {
|
|
|
+ if (StringUtils.isBlank(dictDetail)) {
|
|
|
+ throw new IllegalArgumentException("dictDetail不能为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通过dictDetail模糊查询所有匹配的记录(可能有多个同名数据)
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.like(StoreDictionary::getDictDetail, dictDetail);
|
|
|
+
|
|
|
+ List<StoreDictionary> matchedDicts = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (matchedDicts == null || matchedDicts.isEmpty()) {
|
|
|
+ log.warn("未找到dictDetail为[{}]的记录", dictDetail);
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("找到{}条dictDetail为[{}]的记录", matchedDicts.size(), dictDetail);
|
|
|
+
|
|
|
+ // 用于存储每个根节点对应的所有节点(按根节点分组)
|
|
|
+ Map<Integer, Set<Integer>> rootToNodeIdsMap = new HashMap<>();
|
|
|
+
|
|
|
+ // 遍历所有匹配的记录,收集相关节点ID
|
|
|
+ for (StoreDictionary currentDict : matchedDicts) {
|
|
|
+ Integer parentId = currentDict.getParentId();
|
|
|
+ Integer rootId;
|
|
|
+
|
|
|
+ if (parentId != null && parentId != 0) {
|
|
|
+ // 如果有父级,查找根节点
|
|
|
+ rootId = findRootId(currentDict.getId());
|
|
|
+ log.info("记录ID[{}]有父级[{}], 根节点ID[{}]", currentDict.getId(), parentId, rootId);
|
|
|
+
|
|
|
+ // 收集从根节点到当前节点的路径上的所有节点ID
|
|
|
+ List<Integer> pathNodeIds = new ArrayList<>();
|
|
|
+ collectPathNodes(currentDict.getId(), pathNodeIds);
|
|
|
+ log.info("路径节点IDs: {}", pathNodeIds);
|
|
|
+
|
|
|
+ // 收集该根节点下的所有相关节点ID(包括路径节点及其所有子节点)
|
|
|
+ Set<Integer> nodeIds = rootToNodeIdsMap.computeIfAbsent(rootId, k -> new HashSet<>());
|
|
|
+ for (Integer nodeId : pathNodeIds) {
|
|
|
+ nodeIds.add(nodeId);
|
|
|
+ collectChildrenIds(nodeId, nodeIds);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 如果没有父级,以当前节点为根
|
|
|
+ rootId = currentDict.getId();
|
|
|
+ log.info("记录ID[{}]没有父级, 作为根节点", currentDict.getId());
|
|
|
+
|
|
|
+ // 收集该根节点下的所有相关节点ID
|
|
|
+ Set<Integer> nodeIds = rootToNodeIdsMap.computeIfAbsent(rootId, k -> new HashSet<>());
|
|
|
+ nodeIds.add(currentDict.getId());
|
|
|
+ collectChildrenIds(currentDict.getId(), nodeIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 收集所有需要返回的节点数据
|
|
|
+ Map<Integer, StoreDictionary> allNodeMap = new HashMap<>();
|
|
|
+ for (Set<Integer> nodeIds : rootToNodeIdsMap.values()) {
|
|
|
+ for (Integer nodeId : nodeIds) {
|
|
|
+ if (!allNodeMap.containsKey(nodeId)) {
|
|
|
+ StoreDictionary node = storeDictionaryMapper.selectById(nodeId);
|
|
|
+ if (node != null && node.getDeleteFlag() == 0) {
|
|
|
+ allNodeMap.put(nodeId, node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("总共收集到{}个节点", allNodeMap.size());
|
|
|
+
|
|
|
+ // 构建树形结构,为每个唯一的根节点构建一棵树
|
|
|
+ List<StoreDictionary> resultList = new ArrayList<>();
|
|
|
+
|
|
|
+ for (Map.Entry<Integer, Set<Integer>> entry : rootToNodeIdsMap.entrySet()) {
|
|
|
+ Integer rootId = entry.getKey();
|
|
|
+ Set<Integer> nodeIds = entry.getValue();
|
|
|
+
|
|
|
+ log.info("处理根节点[{}], 包含{}个节点", rootId, nodeIds.size());
|
|
|
+
|
|
|
+ // 从所有节点中筛选出该根节点下的相关节点
|
|
|
+ List<StoreDictionary> relatedNodes = new ArrayList<>();
|
|
|
+ for (Integer nodeId : nodeIds) {
|
|
|
+ StoreDictionary node = allNodeMap.get(nodeId);
|
|
|
+ if (node != null) {
|
|
|
+ relatedNodes.add(node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("根节点[{}]筛选出{}个相关节点", rootId, relatedNodes.size());
|
|
|
+
|
|
|
+ // 如果找到相关节点,构建树形结构
|
|
|
+ if (!relatedNodes.isEmpty()) {
|
|
|
+ StoreDictionary root = buildTreeFromNodeList(relatedNodes, rootId);
|
|
|
+ if (root != null) {
|
|
|
+ resultList.add(root);
|
|
|
+ log.info("成功构建根节点[{}]的树形结构", rootId);
|
|
|
+ } else {
|
|
|
+ log.warn("构建根节点[{}]的树形结构失败", rootId);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ log.warn("根节点[{}]没有相关节点", rootId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("最终返回{}棵树", resultList.size());
|
|
|
+ return resultList;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集节点的所有子节点ID(递归)
|
|
|
+ *
|
|
|
+ * @param nodeId 节点ID
|
|
|
+ * @param childrenIds 子节点ID集合
|
|
|
+ */
|
|
|
+ private void collectChildrenIds(Integer nodeId, Set<Integer> childrenIds) {
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, nodeId);
|
|
|
+
|
|
|
+ List<StoreDictionary> children = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (children != null && !children.isEmpty()) {
|
|
|
+ for (StoreDictionary child : children) {
|
|
|
+ if (!childrenIds.contains(child.getId())) {
|
|
|
+ childrenIds.add(child.getId());
|
|
|
+ collectChildrenIds(child.getId(), childrenIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归查找根节点ID
|
|
|
+ *
|
|
|
+ * @param id 当前节点ID
|
|
|
+ * @return 根节点ID
|
|
|
+ */
|
|
|
+ private Integer findRootId(Integer id) {
|
|
|
+ StoreDictionary dict = storeDictionaryMapper.selectById(id);
|
|
|
+ if (dict == null || dict.getDeleteFlag() == 1) {
|
|
|
+ // 如果节点不存在或已删除,返回当前ID作为根节点
|
|
|
+ return id;
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer parentId = dict.getParentId();
|
|
|
+ if (parentId == null || parentId == 0) {
|
|
|
+ return id;
|
|
|
+ }
|
|
|
+
|
|
|
+ return findRootId(parentId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集从当前节点到根节点的路径上的所有节点ID
|
|
|
+ *
|
|
|
+ * @param nodeId 当前节点ID
|
|
|
+ * @param pathNodeIds 路径节点ID列表
|
|
|
+ */
|
|
|
+ private void collectPathNodes(Integer nodeId, List<Integer> pathNodeIds) {
|
|
|
+ StoreDictionary dict = storeDictionaryMapper.selectById(nodeId);
|
|
|
+ if (dict == null || dict.getDeleteFlag() == 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 避免重复添加
|
|
|
+ if (!pathNodeIds.contains(nodeId)) {
|
|
|
+ pathNodeIds.add(nodeId);
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer parentId = dict.getParentId();
|
|
|
+ if (parentId != null && parentId != 0) {
|
|
|
+ collectPathNodes(parentId, pathNodeIds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从树形结构中过滤出包含指定dictDetail的树
|
|
|
+ *
|
|
|
+ * @param treeList 树形结构列表
|
|
|
+ * @param dictDetail 字典描述
|
|
|
+ * @return 过滤后的树形结构列表
|
|
|
+ */
|
|
|
+ private List<StoreDictionary> filterTreeByDictDetail(List<StoreDictionary> treeList, String dictDetail) {
|
|
|
+ List<StoreDictionary> result = new ArrayList<>();
|
|
|
+
|
|
|
+ for (StoreDictionary node : treeList) {
|
|
|
+ // 检查当前节点是否包含目标dictDetail
|
|
|
+ if (containsDictDetail(node, dictDetail)) {
|
|
|
+ // 如果包含,复制节点并保留树形结构
|
|
|
+ StoreDictionary filteredNode = filterNodeByDictDetail(node, dictDetail);
|
|
|
+ if (filteredNode != null) {
|
|
|
+ result.add(filteredNode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查节点及其子树是否包含指定dictDetail
|
|
|
+ *
|
|
|
+ * @param node 节点
|
|
|
+ * @param dictDetail 字典描述
|
|
|
+ * @return 是否包含
|
|
|
+ */
|
|
|
+ private boolean containsDictDetail(StoreDictionary node, String dictDetail) {
|
|
|
+ if (node == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dictDetail.equals(node.getDictDetail())) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<StoreDictionary> children = node.getStoreDictionaryList();
|
|
|
+ if (children != null && !children.isEmpty()) {
|
|
|
+ for (StoreDictionary child : children) {
|
|
|
+ if (containsDictDetail(child, dictDetail)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 过滤节点,只保留包含目标dictDetail的路径
|
|
|
+ *
|
|
|
+ * @param node 节点
|
|
|
+ * @param dictDetail 字典描述
|
|
|
+ * @return 过滤后的节点
|
|
|
+ */
|
|
|
+ private StoreDictionary filterNodeByDictDetail(StoreDictionary node, String dictDetail) {
|
|
|
+ if (node == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ StoreDictionary result = new StoreDictionary();
|
|
|
+ result.setId(node.getId());
|
|
|
+ result.setTypeName(node.getTypeName());
|
|
|
+ result.setTypeDetail(node.getTypeDetail());
|
|
|
+ result.setParentId(node.getParentId());
|
|
|
+ result.setDictId(node.getDictId());
|
|
|
+ result.setDictDetail(node.getDictDetail());
|
|
|
+ result.setDeleteFlag(node.getDeleteFlag());
|
|
|
+ result.setCreatedTime(node.getCreatedTime());
|
|
|
+ result.setCreatedUserId(node.getCreatedUserId());
|
|
|
+ result.setUpdatedTime(node.getUpdatedTime());
|
|
|
+ result.setUpdatedUserId(node.getUpdatedUserId());
|
|
|
+ result.setHidden(node.getHidden());
|
|
|
+ result.setSortId(node.getSortId());
|
|
|
+
|
|
|
+ List<StoreDictionary> filteredChildren = new ArrayList<>();
|
|
|
+ List<StoreDictionary> children = node.getStoreDictionaryList();
|
|
|
+
|
|
|
+ if (children != null && !children.isEmpty()) {
|
|
|
+ for (StoreDictionary child : children) {
|
|
|
+ if (containsDictDetail(child, dictDetail)) {
|
|
|
+ StoreDictionary filteredChild = filterNodeByDictDetail(child, dictDetail);
|
|
|
+ if (filteredChild != null) {
|
|
|
+ filteredChildren.add(filteredChild);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result.setStoreDictionaryList(filteredChildren);
|
|
|
+
|
|
|
+ // 如果当前节点匹配,或者有子节点,则返回
|
|
|
+ if (dictDetail.equals(node.getDictDetail()) || !filteredChildren.isEmpty()) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集节点及其所有子节点
|
|
|
+ *
|
|
|
+ * @param nodeId 节点ID
|
|
|
+ * @param nodeList 节点列表
|
|
|
+ */
|
|
|
+ private void collectNodeAndChildren(Integer nodeId, List<StoreDictionary> nodeList) {
|
|
|
+ StoreDictionary node = storeDictionaryMapper.selectById(nodeId);
|
|
|
+ if (node == null || node.getDeleteFlag() == 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否已经添加过(避免重复)
|
|
|
+ boolean alreadyAdded = nodeList.stream().anyMatch(n -> n.getId().equals(nodeId));
|
|
|
+ if (!alreadyAdded) {
|
|
|
+ nodeList.add(node);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询所有子节点
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, nodeId);
|
|
|
+ queryWrapper.orderByAsc(StoreDictionary::getSortId);
|
|
|
+
|
|
|
+ List<StoreDictionary> children = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (children != null && !children.isEmpty()) {
|
|
|
+ for (StoreDictionary child : children) {
|
|
|
+ collectNodeAndChildren(child.getId(), nodeList);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 收集节点及其所有子节点(使用Map去重)
|
|
|
+ *
|
|
|
+ * @param nodeId 节点ID
|
|
|
+ * @param nodeMap 节点Map(用于去重)
|
|
|
+ */
|
|
|
+ private void collectNodeAndChildren(Integer nodeId, Map<Integer, StoreDictionary> nodeMap) {
|
|
|
+ StoreDictionary node = storeDictionaryMapper.selectById(nodeId);
|
|
|
+ if (node == null || node.getDeleteFlag() == 1) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到Map(自动去重)
|
|
|
+ nodeMap.put(nodeId, node);
|
|
|
+
|
|
|
+ // 查询所有子节点
|
|
|
+ LambdaQueryWrapper<StoreDictionary> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
+ queryWrapper.in(StoreDictionary::getTypeName, "report", "report_type", "report_classify");
|
|
|
+ queryWrapper.eq(StoreDictionary::getDeleteFlag, 0);
|
|
|
+ queryWrapper.eq(StoreDictionary::getParentId, nodeId);
|
|
|
+ queryWrapper.orderByAsc(StoreDictionary::getSortId);
|
|
|
+
|
|
|
+ List<StoreDictionary> children = storeDictionaryMapper.selectList(queryWrapper);
|
|
|
+
|
|
|
+ if (children != null && !children.isEmpty()) {
|
|
|
+ for (StoreDictionary child : children) {
|
|
|
+ collectNodeAndChildren(child.getId(), nodeMap);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从节点列表构建树形结构
|
|
|
+ *
|
|
|
+ * @param nodeList 节点列表
|
|
|
+ * @param rootId 根节点ID
|
|
|
+ * @return 根节点
|
|
|
+ */
|
|
|
+ private StoreDictionary buildTreeFromNodeList(List<StoreDictionary> nodeList, Integer rootId) {
|
|
|
+ // 创建ID到节点的映射
|
|
|
+ Map<Integer, StoreDictionary> nodeMap = new HashMap<>();
|
|
|
+ for (StoreDictionary node : nodeList) {
|
|
|
+ nodeMap.put(node.getId(), node);
|
|
|
+ node.setStoreDictionaryList(new ArrayList<>());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 找到根节点
|
|
|
+ StoreDictionary root = nodeMap.get(rootId);
|
|
|
+ if (root == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 建立父子关系
|
|
|
+ for (StoreDictionary node : nodeList) {
|
|
|
+ if (node.getId().equals(rootId)) {
|
|
|
+ continue; // 跳过根节点
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer parentId = node.getParentId();
|
|
|
+ if (parentId != null && parentId != 0) {
|
|
|
+ StoreDictionary parent = nodeMap.get(parentId);
|
|
|
+ if (parent != null) {
|
|
|
+ parent.getStoreDictionaryList().add(node);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 对子节点列表按sortId排序
|
|
|
+ sortChildren(root);
|
|
|
+
|
|
|
+ return root;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归排序子节点
|
|
|
+ *
|
|
|
+ * @param node 节点
|
|
|
+ */
|
|
|
+ private void sortChildren(StoreDictionary node) {
|
|
|
+ if (node == null || node.getStoreDictionaryList() == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ node.getStoreDictionaryList().sort((a, b) -> {
|
|
|
+ Integer sortIdA = a.getSortId() != null ? a.getSortId() : 0;
|
|
|
+ Integer sortIdB = b.getSortId() != null ? b.getSortId() : 0;
|
|
|
+ return sortIdA.compareTo(sortIdB);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 递归排序所有子节点
|
|
|
+ for (StoreDictionary child : node.getStoreDictionaryList()) {
|
|
|
+ sortChildren(child);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|