物种分类优化

This commit is contained in:
顾君禹
2025-09-07 15:12:46 +08:00
parent 931c37dfbf
commit af3929c091
8 changed files with 267 additions and 107 deletions

View File

@@ -86,8 +86,8 @@ public class SpeciesLibraryController {
@GetMapping("/tree")
@Operation(summary = "物种库三级树(speciesName1->speciesName2->cultivarName)")
public CommonResult<List<SpeciesThreeLevelTreeRespVO>> getSpeciesThreeLevelTree() {
return success(speciesLibraryService.getSpeciesThreeLevelTree());
public CommonResult<List<SpeciesTwoLevelTreeRespVO>> getSpeciesThreeLevelTree() {
return success(speciesLibraryService.getSpeciesTwoTree(new SpeciesTwoLevelTreeReqVO()));
}
@GetMapping("/two/tree")

View File

@@ -23,12 +23,6 @@ public class SpeciesLibraryRespVO {
@ExcelProperty("物种类别名称")
private String speciesName;
@Schema(description = "物种类别名称-前半段")
private String speciesName1;
@Schema(description = "物种类别名称-后半段")
private String speciesName2;
@Schema(description = "物种名称", example = "芋艿")
@ExcelProperty("物种名称")
private String cultivarName;

View File

@@ -106,9 +106,4 @@ public class SpeciesLibraryDO extends BaseDO {
private String tenantId;
@TableField(exist = false)
private String speciesName1;
@TableField(exist = false)
private String speciesName2;
}

View File

@@ -35,4 +35,6 @@ public interface SpeciesLibraryMapper extends BaseMapperX<SpeciesLibraryDO> {
}
int getSpeciesCount(@Param("tenantId") long tenantId,@Param("baseId") String baseId);
void deleteBySpeciesLibraryId(String id);
}

View File

@@ -40,4 +40,5 @@ public interface TwoDictMapper extends BaseMapperX<TwoDictDO> {
.orderByAsc(TwoDictDO::getId));
}
void deleteByTreeId(String id);
}

View File

@@ -21,10 +21,8 @@ import cn.iocoder.yudao.module.farm.service.sysDict.SysDictService;
import cn.iocoder.yudao.module.farm.service.twodict.TwoDictService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@@ -72,9 +70,12 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
speciesLibrary.setId(id);
speciesLibrary.setTenantId(String.valueOf(user.getTenantId()));
speciesLibrary.setCreateTime(LocalDateTime.now());
TwoDictDO dictDO = twoDictService.getTwoDict(Long.valueOf(speciesLibrary.getCategory()));
TwoDictDO twoDict = twoDictService.getTwoDict(dictDO.getParentId());
speciesLibrary.setSpeciesName(twoDict.getName() + "/" + dictDO.getName());
TwoDictDO dictDO3 = twoDictService.getTwoDict(Long.valueOf(speciesLibrary.getCategory()));
TwoDictDO dictDO2 = twoDictService.getTwoDict(dictDO3.getParentId());
if (dictDO2.getParentId() != null) {
TwoDictDO dictDO1 = twoDictService.getTwoDict(dictDO2.getParentId());
speciesLibrary.setSpeciesName(dictDO1.getName() + "/" + dictDO2.getName() + "/" + dictDO3.getName());
}
speciesLibraryMapper.insert(speciesLibrary);
// 返回
return speciesLibrary.getId();
@@ -123,9 +124,12 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
// 更新
SpeciesLibraryDO updateObj = BeanUtils.toBean(updateReqVO, SpeciesLibraryDO.class);
TwoDictDO dictDO = twoDictService.getTwoDict(Long.valueOf(updateReqVO.getCategory()));
TwoDictDO twoDict = twoDictService.getTwoDict(dictDO.getParentId());
updateObj.setSpeciesName(twoDict.getName() + "/" + dictDO.getName());
TwoDictDO dictDO3 = twoDictService.getTwoDict(Long.valueOf(updateObj.getCategory()));
TwoDictDO dictDO2 = twoDictService.getTwoDict(dictDO3.getParentId());
if (dictDO2.getParentId() != null) {
TwoDictDO dictDO1 = twoDictService.getTwoDict(dictDO2.getParentId());
updateObj.setSpeciesName(dictDO1.getName() + "/" + dictDO2.getName() + "/" + dictDO3.getName());
}
speciesLibraryMapper.updateById(updateObj);
// 同步
// this.syncUpdateDict(updateObj.getId());
@@ -156,10 +160,8 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
public void deleteSpeciesLibrary(String id) {
// 校验存在
validateSpeciesLibraryExists(id);
// 同步
this.syncDeleteDict(id);
// 删除
speciesLibraryMapper.deleteById(id);
speciesLibraryMapper.deleteBySpeciesLibraryId(id);
}
@@ -199,15 +201,6 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
// 触发图片集合转换,确保返回的DO对象包含转换后的图片集合
speciesLibrary.getImageList();
}
if (speciesLibrary != null && speciesLibrary.getSpeciesName() != null) {
String[] parts = speciesLibrary.getSpeciesName().split("/");
if (parts.length > 0) {
speciesLibrary.setSpeciesName1(parts[0]);
}
if (parts.length > 1) {
speciesLibrary.setSpeciesName2(parts[1]);
}
}
return speciesLibrary;
}
@@ -220,15 +213,6 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
if (speciesLibrary != null) {
speciesLibrary.getImageList();
}
if (speciesLibrary != null && speciesLibrary.getSpeciesName() != null) {
String[] parts = speciesLibrary.getSpeciesName().split("/");
if (parts.length > 0) {
speciesLibrary.setSpeciesName1(parts[0]);
}
if (parts.length > 1) {
speciesLibrary.setSpeciesName2(parts[1]);
}
}
});
}
return pageResult;
@@ -378,57 +362,18 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
@Override
public List<SpeciesTwoLevelTreeRespVO> getSpeciesTwoTree(SpeciesTwoLevelTreeReqVO req) {
// 获取当前用户租户ID
Long tenantId = getLoginUser().getTenantId();
// 构建查询条件
LambdaQueryWrapperX<TwoDictDO> wrapperX = new LambdaQueryWrapperX<>();
wrapperX.eq(TwoDictDO::getTenantId, String.valueOf(getLoginUser().getTenantId()));
wrapperX.eqIfPresent(TwoDictDO::getStatus,req.getUseStatus());
wrapperX.orderByDesc(TwoDictDO::getId);
wrapperX.eq(TwoDictDO::getTenantId, String.valueOf(tenantId));
wrapperX.eqIfPresent(TwoDictDO::getStatus, req.getUseStatus());
wrapperX.orderByAsc(TwoDictDO::getParentId).orderByAsc(TwoDictDO::getId);
List<TwoDictDO> allDicts = twoDictMapper.selectList(wrapperX);
List<TwoDictDO> matchedDicts = null;
if (!StringUtils.isEmpty(req.getName() )) {
// 查找匹配的节点
matchedDicts = allDicts.stream().filter(dict -> dict.getName().equals(req.getName())).collect(Collectors.toList());
if (matchedDicts.isEmpty()) {
return Collections.emptyList();
}
} else {
matchedDicts = allDicts;
}
// 构建完整的树结构用于查询
List<SpeciesTwoLevelTreeRespVO> fullTree = buildTree(matchedDicts);
Set<Long> resultIds = new HashSet<>();
List<SpeciesTwoLevelTreeRespVO> result = new ArrayList<>();
for (TwoDictDO matchedDict : matchedDicts) {
if (matchedDict.getParentId() == null) {
// 查询的是父级:找到该父级节点及其所有子节点
SpeciesTwoLevelTreeRespVO parentNode = findNodeById(fullTree, matchedDict.getId());
if (parentNode != null) {
result.add(parentNode);
}
} else {
// 查询的是子类:找到该子类的父级节点
SpeciesTwoLevelTreeRespVO parentNode = findParentNode(fullTree, matchedDict.getParentId());
if (parentNode != null && !resultIds.contains(parentNode.getId())) {
resultIds.add(parentNode.getId());
result.add(parentNode);
}
}
}
return result.stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
SpeciesTwoLevelTreeRespVO::getName,
item -> item,
(existing, replacement) -> existing, // 保留第一个出现的
LinkedHashMap::new
),
map -> new ArrayList<>(map.values())
));
// 构建无限极树形结构
return buildInfiniteTree(allDicts, null, 0);
}
/**
@@ -450,23 +395,240 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
}
/**
* 根据父级ID在树中查找父节点
* 构建无限极树形结构
*
* @param allDicts 所有字典数据
* @param parentId 父级ID
* @param level 当前层级
* @return 树形结构
*/
private SpeciesTwoLevelTreeRespVO findParentNode(List<SpeciesTwoLevelTreeRespVO> tree, Long parentId) {
private List<SpeciesTwoLevelTreeRespVO> buildInfiniteTree(List<TwoDictDO> allDicts, Long parentId, int level) {
List<SpeciesTwoLevelTreeRespVO> treeList = new ArrayList<>();
for (TwoDictDO dict : allDicts) {
// 匹配父级ID,构建当前层级的节点
if (Objects.equals(dict.getParentId(), parentId)) {
SpeciesTwoLevelTreeRespVO treeNode = convertToSpeciesTreeVO(dict);
treeNode.setSort(level);
// 递归构建子节点
List<SpeciesTwoLevelTreeRespVO> children = buildInfiniteTree(allDicts, dict.getId(), level + 1);
treeNode.setChildren(children);
treeList.add(treeNode);
}
}
return treeList;
}
/**
* 将TwoDictDO转换为SpeciesTwoLevelTreeRespVO
*/
private SpeciesTwoLevelTreeRespVO convertToSpeciesTreeVO(TwoDictDO dict) {
SpeciesTwoLevelTreeRespVO vo = new SpeciesTwoLevelTreeRespVO();
vo.setId(dict.getId());
vo.setParentId(dict.getParentId() != null ? dict.getParentId() : 0L);
vo.setName(dict.getName());
vo.setUseStatus(Boolean.TRUE.equals(dict.getStatus()) ? 1 : 0);
return vo;
}
/**
* 构建从指定节点到根节点的完整路径,并过滤掉不相关的分支
*/
private List<SpeciesTwoLevelTreeRespVO> buildPathToRoot(List<SpeciesTwoLevelTreeRespVO> tree, Long targetNodeId) {
// 首先找到目标节点
SpeciesTwoLevelTreeRespVO targetNode = findNodeById(tree, targetNodeId);
if (targetNode == null) {
return new ArrayList<>();
}
// 构建从目标节点到根节点的路径
List<SpeciesTwoLevelTreeRespVO> path = new ArrayList<>();
buildPathRecursively(tree, targetNodeId, path);
// 反转路径,使其从根节点开始
Collections.reverse(path);
// 构建过滤后的树结构
if (!path.isEmpty()) {
return buildFilteredTreeFromPath(path);
}
return new ArrayList<>();
}
/**
* 递归构建从目标节点到根节点的路径
*/
private boolean buildPathRecursively(List<SpeciesTwoLevelTreeRespVO> tree, Long targetNodeId, List<SpeciesTwoLevelTreeRespVO> path) {
for (SpeciesTwoLevelTreeRespVO node : tree) {
if (node.getId().equals(parentId)) {
return node;
if (node.getId().equals(targetNodeId)) {
path.add(node);
return true;
}
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
SpeciesTwoLevelTreeRespVO found = findParentNode(node.getChildren(), parentId);
if (found != null) {
return found;
if (buildPathRecursively(node.getChildren(), targetNodeId, path)) {
path.add(node);
return true;
}
}
}
return false;
}
/**
* 根据路径构建过滤后的树结构
*/
private List<SpeciesTwoLevelTreeRespVO> buildFilteredTreeFromPath(List<SpeciesTwoLevelTreeRespVO> path) {
if (path.isEmpty()) {
return new ArrayList<>();
}
// 从根节点开始构建过滤后的树
SpeciesTwoLevelTreeRespVO rootNode = createFilteredNodeFromPath(path, 0);
return Collections.singletonList(rootNode);
}
/**
* 递归创建过滤后的节点,只包含路径上的节点
*/
private SpeciesTwoLevelTreeRespVO createFilteredNodeFromPath(List<SpeciesTwoLevelTreeRespVO> path, int currentIndex) {
if (currentIndex >= path.size()) {
return null;
}
SpeciesTwoLevelTreeRespVO originalNode = path.get(currentIndex);
SpeciesTwoLevelTreeRespVO filteredNode = new SpeciesTwoLevelTreeRespVO();
filteredNode.setId(originalNode.getId());
filteredNode.setParentId(originalNode.getParentId());
filteredNode.setName(originalNode.getName());
filteredNode.setUseStatus(originalNode.getUseStatus());
filteredNode.setSort(originalNode.getSort());
// 如果有下一个节点,说明当前节点有子节点
if (currentIndex + 1 < path.size()) {
SpeciesTwoLevelTreeRespVO childNode = createFilteredNodeFromPath(path, currentIndex + 1);
if (childNode != null) {
filteredNode.setChildren(Collections.singletonList(childNode));
} else {
filteredNode.setChildren(new ArrayList<>());
}
} else {
// 这是路径的最后一个节点,没有子节点
filteredNode.setChildren(new ArrayList<>());
}
return filteredNode;
}
/**
* 找到指定节点的根节点ID
*/
private Long findRootNodeId(List<SpeciesTwoLevelTreeRespVO> tree, Long targetNodeId) {
// 首先找到目标节点
SpeciesTwoLevelTreeRespVO targetNode = findNodeById(tree, targetNodeId);
if (targetNode == null) {
return null;
}
// 如果目标节点就是根节点
if (targetNode.getParentId() == null || targetNode.getParentId() == 0L) {
return targetNode.getId();
}
// 递归向上查找根节点
return findRootNodeIdRecursively(tree, targetNode.getParentId());
}
/**
* 递归查找根节点ID
*/
private Long findRootNodeIdRecursively(List<SpeciesTwoLevelTreeRespVO> tree, Long nodeId) {
for (SpeciesTwoLevelTreeRespVO node : tree) {
if (node.getId().equals(nodeId)) {
if (node.getParentId() == null || node.getParentId() == 0L) {
return node.getId();
} else {
return findRootNodeIdRecursively(tree, node.getParentId());
}
}
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
Long result = findRootNodeIdRecursively(node.getChildren(), nodeId);
if (result != null) {
return result;
}
}
}
return null;
}
/**
* 创建过滤后的根节点,只包含匹配的子节点路径
*/
private SpeciesTwoLevelTreeRespVO createFilteredRootNode(SpeciesTwoLevelTreeRespVO rootNode, List<Long> matchedChildIds) {
SpeciesTwoLevelTreeRespVO filteredRootNode = new SpeciesTwoLevelTreeRespVO();
filteredRootNode.setId(rootNode.getId());
filteredRootNode.setParentId(rootNode.getParentId());
filteredRootNode.setName(rootNode.getName());
filteredRootNode.setUseStatus(rootNode.getUseStatus());
filteredRootNode.setSort(rootNode.getSort());
// 递归过滤子节点
List<SpeciesTwoLevelTreeRespVO> filteredChildren = filterChildrenRecursively(rootNode.getChildren(), matchedChildIds);
filteredRootNode.setChildren(filteredChildren);
return filteredRootNode;
}
/**
* 递归过滤子节点,只保留包含匹配节点的路径
*/
private List<SpeciesTwoLevelTreeRespVO> filterChildrenRecursively(List<SpeciesTwoLevelTreeRespVO> children, List<Long> matchedChildIds) {
if (children == null || children.isEmpty()) {
return new ArrayList<>();
}
List<SpeciesTwoLevelTreeRespVO> filteredChildren = new ArrayList<>();
for (SpeciesTwoLevelTreeRespVO child : children) {
// 检查当前节点是否在匹配列表中
boolean isMatched = matchedChildIds.contains(child.getId());
// 递归检查子节点
List<SpeciesTwoLevelTreeRespVO> filteredGrandChildren = filterChildrenRecursively(child.getChildren(), matchedChildIds);
// 如果当前节点匹配或有匹配的子节点,则包含此节点
if (isMatched || !filteredGrandChildren.isEmpty()) {
SpeciesTwoLevelTreeRespVO filteredChild = new SpeciesTwoLevelTreeRespVO();
filteredChild.setId(child.getId());
filteredChild.setParentId(child.getParentId());
filteredChild.setName(child.getName());
filteredChild.setUseStatus(child.getUseStatus());
filteredChild.setSort(child.getSort());
filteredChild.setChildren(filteredGrandChildren);
filteredChildren.add(filteredChild);
}
}
return filteredChildren;
}
/**
* 收集指定节点的所有后代节点ID
*/
private void collectAllDescendants(List<SpeciesTwoLevelTreeRespVO> tree, Long nodeId, Set<Long> allDescendants) {
SpeciesTwoLevelTreeRespVO node = findNodeById(tree, nodeId);
if (node != null && node.getChildren() != null) {
for (SpeciesTwoLevelTreeRespVO child : node.getChildren()) {
allDescendants.add(child.getId());
// 递归收集子节点的后代
collectAllDescendants(tree, child.getId(), allDescendants);
}
}
}
/**
* 构建完整的树形结构(之前的实现)
*/
@@ -509,7 +671,7 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
vo.setId(dict.getId());
vo.setParentId(dict.getParentId() != null ? dict.getParentId() : 0L);
vo.setName(dict.getName());
vo.setUseStatus(dict.getStatus() ? 1 :0);
vo.setUseStatus(dict.getStatus() ? 1 : 0);
return vo;
}
@@ -598,7 +760,7 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
updateSelfAndChildren(id, req.getUseStatus() == 1);
} else {
// 4. 如果是子节点,只更新自身
this.updateSelf(id,req.getName(),req.getUseStatus() ==1);
this.updateSelf(id, req.getName(), req.getUseStatus() == 1);
}
return true;
} catch (Exception e) {
@@ -627,9 +789,9 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
/**
* 更新自身及其所有子节点(递归更新)
*/
private void updateSelfAndChildren(Long id,boolean status) {
private void updateSelfAndChildren(Long id, boolean status) {
// 先更新自身
updateSelf(id,null,status);
updateSelf(id, null, status);
// 递归更新所有子节点
updateChildrenRecursively(id, status);
}
@@ -646,7 +808,7 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
for (TwoDictDO child : children) {
// 更新当前子节点
updateSelf(child.getId(),null, useStatus);
updateSelf(child.getId(), null, useStatus);
// 递归更新当前子节点的子节点(如果有)
if (hasChildren(child.getId())) {
@@ -658,11 +820,7 @@ public class SpeciesLibraryServiceImpl implements SpeciesLibraryService {
@Override
public boolean deleteSpeciesTwoLevelTree(String id) {
try {
LambdaUpdateWrapper<TwoDictDO> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(TwoDictDO::getId, id)
.eq(TwoDictDO::getDeleted, false) // 只更新未删除的记录
.set(TwoDictDO::getDeleted, true); // 设置删除状态
twoDictMapper.update(updateWrapper);
twoDictMapper.deleteByTreeId(id);
return true;
} catch (Exception e) {
return false;

View File

@@ -2,6 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.farm.dal.mysql.specieslibrary.SpeciesLibraryMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
@@ -18,5 +19,9 @@
<if test="baseId != null and baseId !=''">AND base_id = #{baseId}</if>
</where>
</select>
<delete id="deleteBySpeciesLibraryId" parameterType="String">
DELETE
FROM farm_species_library
WHERE id = #{id}
</delete>
</mapper>

View File

@@ -9,4 +9,9 @@
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
-->
<delete id="deleteByTreeId" parameterType="String">
delete
from farm_two_dict
where id = #{id}
</delete>
</mapper>