From daf30766ba17185ff8ced896b8894e1dfc7a355c Mon Sep 17 00:00:00 2001 From: douxu Date: Tue, 13 May 2025 16:34:25 +0800 Subject: [PATCH] refactor(topologic storage struct): refactor topologic storage struct 1.refactor topologic storage struct by multi branch tree 2.add new func of build multi branch tree 3.modify sql of query topologic from db 4.delete page id field from topologic struct --- constant/togologic.go | 10 +++ database/create_topologic.go | 1 - database/query_component.go | 1 + database/query_topologic.go | 146 ++++++++++++++++++++++++------- database/update_topologic.go | 1 - diagram/multi_branch_tree.go | 64 ++++++++++++++ main.go | 24 +---- orm/circuit_diagram_topologic.go | 1 - sql/topologic.go | 7 +- test/orm/topologic_test.go | 3 +- 10 files changed, 192 insertions(+), 66 deletions(-) create mode 100644 diagram/multi_branch_tree.go diff --git a/constant/togologic.go b/constant/togologic.go index 871ffc5..7ae90b0 100644 --- a/constant/togologic.go +++ b/constant/togologic.go @@ -1,5 +1,7 @@ package constant +import "github.com/gofrs/uuid" + const ( // UUIDErrChangeType 拓扑信息错误改变类型 UUIDErrChangeType = iota @@ -10,3 +12,11 @@ const ( // UUIDAddChangeType 拓扑信息新增类型 UUIDAddChangeType ) + +const ( + // SpecialUUIDStr 拓扑信息中开始节点与结束节点字符串形式 + SpecialUUIDStr = "00000000-0000-0000-0000-000000000000" +) + +// SpecialUUID 拓扑信息中开始节点与结束节点 UUID 格式 +var SpecialUUID = uuid.FromStringOrNil(SpecialUUIDStr) diff --git a/database/create_topologic.go b/database/create_topologic.go index 5af336c..57d2727 100644 --- a/database/create_topologic.go +++ b/database/create_topologic.go @@ -21,7 +21,6 @@ func CreateTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, topol var topologicSlice []orm.Topologic for _, info := range topologicInfos { topologicInfo := orm.Topologic{ - PageID: pageID, UUIDFrom: info.UUIDFrom, UUIDTo: info.UUIDTo, Flag: info.Flag, diff --git a/database/query_component.go b/database/query_component.go index 1d44f20..21151c8 100644 --- a/database/query_component.go +++ b/database/query_component.go @@ -28,6 +28,7 @@ func QueryCircuitDiagramComponentFromDB(ctx context.Context, tx *gorm.DB, pool * return nil, result.Error } + // TODO 优化componentTypeMap输出 componentTypeMap := make(map[uuid.UUID]int, len(components)) for _, component := range components { diff --git a/database/query_topologic.go b/database/query_topologic.go index 1aeecca..bfae1eb 100644 --- a/database/query_topologic.go +++ b/database/query_topologic.go @@ -3,8 +3,10 @@ package database import ( "context" + "fmt" "time" + "modelRT/constant" "modelRT/diagram" "modelRT/orm" "modelRT/sql" @@ -15,16 +17,16 @@ import ( "gorm.io/gorm/clause" ) -// QueryTopologicByPageID return the topologic info of the circuit diagram query by pageID -func QueryTopologicByPageID(ctx context.Context, tx *gorm.DB, logger *zap.Logger, pageID int64) ([]orm.Topologic, error) { +// QueryTopologic return the topologic info of the circuit diagram +func QueryTopologic(ctx context.Context, tx *gorm.DB, logger *zap.Logger) ([]orm.Topologic, error) { var topologics []orm.Topologic // ctx超时判断 cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() - result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Raw(sql.RecursiveSQL, pageID).Scan(&topologics) + result := tx.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Raw(sql.RecursiveSQL, constant.SpecialUUIDStr).Scan(&topologics) if result.Error != nil { - logger.Error("query circuit diagram topologic info by pageID failed", zap.Int64("pageID", pageID), zap.Error(result.Error)) + logger.Error("query circuit diagram topologic info by start node uuid failed", zap.String("start_node_uuid", constant.SpecialUUIDStr), zap.Error(result.Error)) return nil, result.Error } return topologics, nil @@ -32,58 +34,136 @@ func QueryTopologicByPageID(ctx context.Context, tx *gorm.DB, logger *zap.Logger // TODO 电流互感器不单独划分间隔 // TODO 以母线、浇筑母线、变压器为间隔原件 -// QueryTopologicFromDB return the result of query topologic info from postgresDB -func QueryTopologicFromDB(ctx context.Context, tx *gorm.DB, logger *zap.Logger, gridID, zoneID, stationID int64) ([]orm.Page, error) { - allPages, err := QueryAllPages(ctx, tx, logger, gridID, zoneID, stationID) +// QueryTopologicFromDB return the result of query topologic info from DB +func QueryTopologicFromDB(ctx context.Context, tx *gorm.DB, logger *zap.Logger, componentTypeMap map[uuid.UUID]int) error { + topologicInfos, err := QueryTopologic(ctx, tx, logger) if err != nil { - logger.Error("query all pages info failed", zap.Int64("gridID", gridID), zap.Int64("zoneID", zoneID), zap.Int64("stationID", stationID), zap.Error(err)) - return nil, err + logger.Error("query topologic info failed", zap.Error(err)) + return err } - for _, page := range allPages { - topologicInfos, err := QueryTopologicByPageID(ctx, tx, logger, page.ID) - if err != nil { - logger.Error("query topologic info by pageID failed", zap.Int64("pageID", page.ID), zap.Error(err)) - return nil, err - } + // err = InitCircuitDiagramTopologic(topologicInfos, componentTypeMap) + // if err != nil { + // logger.Error("init topologic failed", zap.Error(err)) + // return err + // } - err = InitCircuitDiagramTopologic(page.ID, topologicInfos) - if err != nil { - logger.Error("init topologic failed", zap.Error(err)) - return nil, err - } + _, err = BuildMultiBranchTree(topologicInfos, componentTypeMap) + if err != nil { + logger.Error("init topologic failed", zap.Error(err)) + return err } - return allPages, nil + return nil } // InitCircuitDiagramTopologic return circuit diagram topologic info from postgres -func InitCircuitDiagramTopologic(pageID int64, topologicNodes []orm.Topologic) error { - var rootVertex uuid.UUID - +func InitCircuitDiagramTopologic(topologicNodes []orm.Topologic, componentTypeMap map[uuid.UUID]int) error { + var rootVertex *diagram.MultiBranchTreeNode for _, node := range topologicNodes { - if node.UUIDFrom.IsNil() { - rootVertex = node.UUIDTo + if node.UUIDFrom == constant.SpecialUUID { + // rootVertex = node.UUIDTo + var componentType int + componentType, ok := componentTypeMap[node.UUIDFrom] + if !ok { + return fmt.Errorf("can not get component type by uuid: %s", node.UUIDFrom) + } + rootVertex = diagram.NewMultiBranchTree(node.UUIDFrom, componentType) break } } - topologicSet := diagram.NewGraph(rootVertex) + if rootVertex == nil { + return fmt.Errorf("root vertex is nil") + } for _, node := range topologicNodes { - if node.UUIDFrom.IsNil() { - continue + if node.UUIDFrom == constant.SpecialUUID { + var componentType int + componentType, ok := componentTypeMap[node.UUIDTo] + if !ok { + return fmt.Errorf("can not get component type by uuid: %s", node.UUIDTo) + } + nodeVertex := diagram.NewMultiBranchTree(node.UUIDTo, componentType) + + rootVertex.AddChild(nodeVertex) } - // TODO 增加对 node.flag值的判断 - topologicSet.AddEdge(node.UUIDFrom, node.UUIDTo) } - diagram.StoreGraphMap(pageID, topologicSet) + + node := rootVertex + for _, nodeVertex := range node.Children { + nextVertexs := make([]*diagram.MultiBranchTreeNode, 0) + nextVertexs = append(nextVertexs, nodeVertex) + } return nil } -func IntervalBoundaryDetermine(pageID int64, uuid uuid.UUID) bool { +func IntervalBoundaryDetermine(uuid uuid.UUID) bool { // TODO 从diagramsOverview中根据 uuid 获取 component 信息 var componentID int64 diagram.GetComponentMap(componentID) // TODO 判断 component 的类型是否为间隔 return true } + +// BuildMultiBranchTree return the multi branch tree by topologic info and component type map +func BuildMultiBranchTree(topologics []orm.Topologic, componentTypeMap map[uuid.UUID]int) (*diagram.MultiBranchTreeNode, error) { + nodeMap := make(map[uuid.UUID]*diagram.MultiBranchTreeNode, len(topologics)) + + for _, topo := range topologics { + // skip special uuid + if topo.UUIDFrom != constant.SpecialUUID { + if _, exists := nodeMap[topo.UUIDFrom]; !exists { + componentType, ok := componentTypeMap[topo.UUIDFrom] + if !ok { + return nil, fmt.Errorf("can not get component type by uuid: %s", topo.UUIDFrom) + } + + nodeMap[topo.UUIDFrom] = &diagram.MultiBranchTreeNode{ + ID: topo.UUIDFrom, + NodeComponentType: componentType, + } + } + } + + if _, exists := nodeMap[topo.UUIDTo]; !exists { + componentType, ok := componentTypeMap[topo.UUIDTo] + if !ok { + return nil, fmt.Errorf("can not get component type by uuid: %s", topo.UUIDTo) + } + + nodeMap[topo.UUIDTo] = &diagram.MultiBranchTreeNode{ + ID: topo.UUIDTo, + NodeComponentType: componentType, + } + } + } + + for _, topo := range topologics { + var parent *diagram.MultiBranchTreeNode + if topo.UUIDFrom == constant.SpecialUUID { + componentType, ok := componentTypeMap[topo.UUIDTo] + if !ok { + return nil, fmt.Errorf("can not get component type by uuid: %s", topo.UUIDTo) + } + + parent = &diagram.MultiBranchTreeNode{ + ID: constant.SpecialUUID, + NodeComponentType: componentType, + } + nodeMap[constant.SpecialUUID] = parent + } else { + parent = nodeMap[topo.UUIDFrom] + } + + child := nodeMap[topo.UUIDTo] + child.Parent = parent + parent.Children = append(parent.Children, child) + } + + // return root vertex + root, exists := nodeMap[constant.SpecialUUID] + if !exists { + return nil, fmt.Errorf("root node not found") + } + return root, nil +} diff --git a/database/update_topologic.go b/database/update_topologic.go index d64a674..c077d31 100644 --- a/database/update_topologic.go +++ b/database/update_topologic.go @@ -47,7 +47,6 @@ func UpdateTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, chang result = tx.WithContext(cancelCtx).Model(&orm.Topologic{}).Where("page_id = ? and uuid_from = ? and uuid_to = ?", pageID, changeInfo.OldUUIDFrom, changeInfo.OldUUIDTo).Updates(&orm.Topologic{UUIDTo: changeInfo.NewUUIDTo}) case constant.UUIDAddChangeType: topologic := orm.Topologic{ - PageID: pageID, Flag: changeInfo.Flag, UUIDFrom: changeInfo.NewUUIDFrom, UUIDTo: changeInfo.NewUUIDTo, diff --git a/diagram/multi_branch_tree.go b/diagram/multi_branch_tree.go new file mode 100644 index 0000000..7de6f03 --- /dev/null +++ b/diagram/multi_branch_tree.go @@ -0,0 +1,64 @@ +package diagram + +import ( + "fmt" + + "github.com/gofrs/uuid" +) + +// MultiBranchTreeNode represents a topological structure using an multi branch tree +type MultiBranchTreeNode struct { + ID uuid.UUID // 节点唯一标识 + NodeComponentType int // 节点组件类型 + Parent *MultiBranchTreeNode // 指向父节点的指针 + Children []*MultiBranchTreeNode // 指向所有子节点的指针切片 +} + +func NewMultiBranchTree(id uuid.UUID, componentType int) *MultiBranchTreeNode { + return &MultiBranchTreeNode{ + ID: id, + NodeComponentType: componentType, + Children: make([]*MultiBranchTreeNode, 0), + } +} + +func (n *MultiBranchTreeNode) AddChild(child *MultiBranchTreeNode) { + child.Parent = n + n.Children = append(n.Children, child) +} + +func (n *MultiBranchTreeNode) RemoveChild(childID uuid.UUID) bool { + for i, child := range n.Children { + if child.ID == childID { + n.Children = append(n.Children[:i], n.Children[i+1:]...) + child.Parent = nil + return true + } + } + return false +} + +func (n *MultiBranchTreeNode) FindNodeByID(id uuid.UUID) *MultiBranchTreeNode { + if n.ID == id { + return n + } + + for _, child := range n.Children { + if found := child.FindNodeByID(id); found != nil { + return found + } + } + return nil +} + +func (n *MultiBranchTreeNode) PrintTree(level int) { + for i := 0; i < level; i++ { + fmt.Print(" ") + } + + fmt.Printf("- ComponentType:%d,(ID: %s)\n", n.NodeComponentType, n.ID) + + for _, child := range n.Children { + child.PrintTree(level + 1) + } +} diff --git a/main.go b/main.go index b185b00..cee65ef 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ package main import ( "context" "flag" - "fmt" "time" "modelRT/alert" @@ -109,32 +108,11 @@ func main() { } // TODO 暂时屏蔽完成 swagger 启动测试 - // TODO 将componentTypeMap传入QueryTopologicFromDB中 - pages, err := database.QueryTopologicFromDB(ctx, tx, zapLogger, modelRTConfig.GridID, modelRTConfig.ZoneID, modelRTConfig.StationID) + err = database.QueryTopologicFromDB(ctx, tx, zapLogger, componentTypeMap) if err != nil { zapLogger.Error("load topologic info from postgres failed", zap.Error(err)) panic(err) } - - for _, page := range pages { - graph, err := diagram.GetGraphMap(page.ID) - if err != nil { - // TODO 增加报错日志错误 - continue - } - - rootNode := graph.RootVertex.String() - links := graph.VerticeLinks[rootNode] - for { - for _, link := range links { - fmt.Println(link) - } - // TODO 重置 links - } - - } - fmt.Println(componentTypeMap) - return nil }) diff --git a/orm/circuit_diagram_topologic.go b/orm/circuit_diagram_topologic.go index 5b4b5b0..08bd4b3 100644 --- a/orm/circuit_diagram_topologic.go +++ b/orm/circuit_diagram_topologic.go @@ -6,7 +6,6 @@ import "github.com/gofrs/uuid" // Topologic structure define topologic info set of circuit diagram type Topologic struct { ID int64 `gorm:"column:id"` - PageID int64 `gorm:"column:page_id"` Flag int `gorm:"column:flag"` UUIDFrom uuid.UUID `gorm:"column:uuid_from"` UUIDTo uuid.UUID `gorm:"column:uuid_to"` diff --git a/sql/topologic.go b/sql/topologic.go index 58be916..95f292d 100644 --- a/sql/topologic.go +++ b/sql/topologic.go @@ -3,15 +3,12 @@ package sql // RecursiveSQL define Topologic table recursive query statement var RecursiveSQL = `WITH RECURSIVE recursive_tree as ( - SELECT uuid_from,uuid_to,page_id,flag + SELECT uuid_from,uuid_to,flag FROM "Topologic" - WHERE uuid_from is null and page_id = ? + WHERE uuid_from = ? UNION ALL SELECT t.uuid_from,t.uuid_to,t.page_id,t.flag FROM "Topologic" t JOIN recursive_tree rt ON t.uuid_from = rt.uuid_to ) SELECT * FROM recursive_tree;` - -// TODO 为 Topologic 表增加唯一索引 -// CREATE UNIQUE INDEX uuid_from_to_page_id_idx ON public."Topologic"(uuid_from,uuid_to,page_id); diff --git a/test/orm/topologic_test.go b/test/orm/topologic_test.go index c107d35..f5443fc 100644 --- a/test/orm/topologic_test.go +++ b/test/orm/topologic_test.go @@ -41,7 +41,6 @@ func TestMain(m *testing.M) { func TestUserDao_CreateUser(t *testing.T) { topologicInfo := &orm.Topologic{ - PageID: 1, UUIDFrom: uuid.FromStringOrNil("70c190f2-8a60-42a9-b143-ec5f87e0aa6b"), UUIDTo: uuid.FromStringOrNil("70c190f2-8a75-42a9-b166-ec5f87e0aa6b"), Comment: "test", @@ -51,7 +50,7 @@ func TestUserDao_CreateUser(t *testing.T) { // ud := dao2.NewUserDao(context.TODO()) mock.ExpectBegin() mock.ExpectExec(regexp.QuoteMeta("INSERT INTO `Topologic`")). - WithArgs(topologicInfo.PageID, topologicInfo.Flag, topologicInfo.UUIDFrom, topologicInfo.UUIDTo, topologicInfo.Comment). + WithArgs(topologicInfo.Flag, topologicInfo.UUIDFrom, topologicInfo.UUIDTo, topologicInfo.Comment). WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit()