From 2bc22a55d60edf297cb305c662b42c17d15e0660 Mon Sep 17 00:00:00 2001 From: gaozheng Date: Tue, 9 Jun 2026 11:33:14 +0800 Subject: [PATCH] =?UTF-8?q?feat(data):=20buildStructTree=20=E6=89=81?= =?UTF-8?q?=E5=B9=B3=E2=86=92=E6=A0=91=EF=BC=88=E5=8F=B6=E5=AD=90=3DTM?= =?UTF-8?q?=EF=BC=8C=E5=90=AB=E7=9B=B4=E6=8C=82/=E5=AD=A4=E5=84=BF/?= =?UTF-8?q?=E7=A9=BA=E8=A1=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/data/dto/NavDto.cpp | 34 +++++++++++++++++++++++++++++++++- tests/data/test_nav_dto.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/data/dto/NavDto.cpp b/src/data/dto/NavDto.cpp index 57f43b1..2241804 100644 --- a/src/data/dto/NavDto.cpp +++ b/src/data/dto/NavDto.cpp @@ -2,6 +2,9 @@ #include +#include +#include + namespace geopro::data::dto { namespace { @@ -75,6 +78,35 @@ std::vector parseDatasets(const QJsonArray& arr) { return out; } -std::vector buildStructTree(const std::vector&) { return {}; } +std::vector buildStructTree(const std::vector& flat) { + std::set ids; + std::set hasChild; + for (const auto& n : flat) { + ids.insert(n.id); + if (!n.parentId.empty()) hasChild.insert(n.parentId); + } + // 叶子(无子节点)= TM。 + auto isLeaf = [&](const std::string& id) { return hasChild.find(id) == hasChild.end(); }; + // 根层节点:parentId 为空或 parentId 不在 id 集合内(孤儿)。 + auto isRootLevel = [&](const StructNode& n) { + return n.parentId.empty() || ids.find(n.parentId) == ids.end(); + }; + std::function(const std::string&, bool)> build = + [&](const std::string& parentId, bool root) { + std::vector out; + for (const auto& n : flat) { + const bool belongs = root ? isRootLevel(n) : (n.parentId == parentId); + if (!belongs) continue; + if (n.id == parentId) continue; // 防自环 + StructTreeNode t; + t.node = n; + t.isTm = isLeaf(n.id); + t.children = build(n.id, false); + out.push_back(std::move(t)); + } + return out; + }; + return build(std::string(), true); +} } // namespace geopro::data::dto diff --git a/tests/data/test_nav_dto.cpp b/tests/data/test_nav_dto.cpp index 02cfc3e..878901f 100644 --- a/tests/data/test_nav_dto.cpp +++ b/tests/data/test_nav_dto.cpp @@ -74,3 +74,35 @@ TEST(NavDto, ParseDatasetsMapsDdCodeToDdType) { EXPECT_EQ(ds[0].name, "批次1"); EXPECT_EQ(ds[0].ddType, "dd_section"); } + +TEST(NavDto, BuildStructTreeNestsGsTmAndDirectTm) { + const std::vector flat = { + {"gs1", "工区1", "", "GS", "", 1}, + {"tm1", "测线1", "gs1", "TM", "", 2}, + {"tm2", "测线2", "gs1", "TM", "", 2}, + {"tmD", "直挂测线", "", "TM", "", 2}, // TM 直挂项目(无 GS) + }; + const auto roots = dto::buildStructTree(flat); + ASSERT_EQ(roots.size(), 2u); // gs1 + tmD + EXPECT_EQ(roots[0].node.id, "gs1"); + EXPECT_FALSE(roots[0].isTm); // 非叶 = GS + ASSERT_EQ(roots[0].children.size(), 2u); + EXPECT_EQ(roots[0].children[0].node.id, "tm1"); + EXPECT_TRUE(roots[0].children[0].isTm); // 叶 = TM + EXPECT_EQ(roots[1].node.id, "tmD"); + EXPECT_TRUE(roots[1].isTm); // 直挂项目的叶子 = TM +} + +TEST(NavDto, BuildStructTreeOrphanParentBecomesRoot) { + const std::vector flat = { + {"tmX", "孤儿测线", "ghost", "TM", "", 2}, // parentId 不在集合内 + }; + const auto roots = dto::buildStructTree(flat); + ASSERT_EQ(roots.size(), 1u); + EXPECT_EQ(roots[0].node.id, "tmX"); + EXPECT_TRUE(roots[0].isTm); +} + +TEST(NavDto, BuildStructTreeEmpty) { + EXPECT_TRUE(dto::buildStructTree({}).empty()); +}