feat(data): buildStructTree 扁平→树(叶子=TM,含直挂/孤儿/空表)
This commit is contained in:
parent
a32bd763da
commit
2bc22a55d6
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
#include <QJsonValue>
|
#include <QJsonValue>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace geopro::data::dto {
|
namespace geopro::data::dto {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -75,6 +78,35 @@ std::vector<DsNode> parseDatasets(const QJsonArray& arr) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StructTreeNode> buildStructTree(const std::vector<StructNode>&) { return {}; }
|
std::vector<StructTreeNode> buildStructTree(const std::vector<StructNode>& flat) {
|
||||||
|
std::set<std::string> ids;
|
||||||
|
std::set<std::string> 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<std::vector<StructTreeNode>(const std::string&, bool)> build =
|
||||||
|
[&](const std::string& parentId, bool root) {
|
||||||
|
std::vector<StructTreeNode> 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
|
} // namespace geopro::data::dto
|
||||||
|
|
|
||||||
|
|
@ -74,3 +74,35 @@ TEST(NavDto, ParseDatasetsMapsDdCodeToDdType) {
|
||||||
EXPECT_EQ(ds[0].name, "批次1");
|
EXPECT_EQ(ds[0].name, "批次1");
|
||||||
EXPECT_EQ(ds[0].ddType, "dd_section");
|
EXPECT_EQ(ds[0].ddType, "dd_section");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(NavDto, BuildStructTreeNestsGsTmAndDirectTm) {
|
||||||
|
const std::vector<StructNode> 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<StructNode> 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());
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue