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
This commit is contained in:
douxu 2025-05-13 16:34:25 +08:00
parent af0cfce78f
commit daf30766ba
10 changed files with 192 additions and 66 deletions

View File

@ -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)

View File

@ -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,

View File

@ -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 {

View File

@ -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
}

View File

@ -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,

View File

@ -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)
}
}

24
main.go
View File

@ -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
})

View File

@ -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"`

View File

@ -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);

View File

@ -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()