diff --git a/circuit_diagram_update.json b/circuit_diagram_update.json deleted file mode 100644 index 56be392..0000000 --- a/circuit_diagram_update.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "page_id":1, - "topologics":[ - { - "from_uuid":"12311-111", - "to_uuid":"12311-113" - }, - { - "from_uuid":"12311-111", - "to_uuid":"12311-114" - } - ], - "component_infos":[ - { - "uuid":"12311-113", - "component_type":1, - "params":"" - }, - { - "uuid":"12311-114", - "component_type":1, - "params":"" - } - ] -} diff --git a/constant/error.go b/constant/error.go new file mode 100644 index 0000000..5378510 --- /dev/null +++ b/constant/error.go @@ -0,0 +1,23 @@ +package constant + +import "errors" + +// ErrUUIDChangeType define error of check uuid from value failed in uuid from change type +var ErrUUIDChangeType = errors.New("undefined uuid change type") + +var ( + // ErrUUIDFromCheckT1 define error of check uuid from value failed in uuid from change type + ErrUUIDFromCheckT1 = errors.New("in uuid from change type, value of new uuid_from is equal value of old uuid_from") + // ErrUUIDToCheckT1 define error of check uuid to value failed in uuid from change type + ErrUUIDToCheckT1 = errors.New("in uuid from change type, value of new uuid_to is not equal value of old uuid_to") + + // ErrUUIDFromCheckT2 define error of check uuid from value failed in uuid to change type + ErrUUIDFromCheckT2 = errors.New("in uuid to change type, value of new uuid_from is not equal value of old uuid_from") + // ErrUUIDToCheckT2 define error of check uuid to value failed in uuid to change type + ErrUUIDToCheckT2 = errors.New("in uuid to change type, value of new uuid_to is equal value of old uuid_to") + + // ErrUUIDFromCheckT3 define error of check uuid from value failed in uuid add change type + ErrUUIDFromCheckT3 = errors.New("in uuid add change type, value of old uuid_from is not empty") + // ErrUUIDToCheckT3 define error of check uuid to value failed in uuid add change type + ErrUUIDToCheckT3 = errors.New("in uuid add change type, value of old uuid_to is not empty") +) diff --git a/constant/togologic.go b/constant/togologic.go new file mode 100644 index 0000000..871ffc5 --- /dev/null +++ b/constant/togologic.go @@ -0,0 +1,12 @@ +package constant + +const ( + // UUIDErrChangeType 拓扑信息错误改变类型 + UUIDErrChangeType = iota + // UUIDFromChangeType 拓扑信息父节点改变类型 + UUIDFromChangeType + // UUIDToChangeType 拓扑信息子节点改变类型 + UUIDToChangeType + // UUIDAddChangeType 拓扑信息新增类型 + UUIDAddChangeType +) diff --git a/database/query_topologic.go b/database/query_topologic.go index 7e2f6fd..2224431 100644 --- a/database/query_topologic.go +++ b/database/query_topologic.go @@ -7,30 +7,20 @@ import ( "modelRT/diagram" "modelRT/orm" + "modelRT/sql" "github.com/gofrs/uuid" "go.uber.org/zap" "gorm.io/gorm/clause" ) -var recursiveSQL = `WITH RECURSIVE recursive_tree as ( - SELECT uuid_from,uuid_to,page_id,flag - FROM "Topologic" - WHERE uuid_from is null and page_id = ? - 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;` - // QueryTopologicByPageID return the topologic info of the circuit diagram query by pageID func QueryTopologicByPageID(ctx context.Context, logger *zap.Logger, pageID int64) ([]orm.Topologic, error) { var topologics []orm.Topologic // ctx超时判断 cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() - result := _globalPostgresClient.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Raw(recursiveSQL, pageID).Scan(&topologics) + result := _globalPostgresClient.WithContext(cancelCtx).Clauses(clause.Locking{Strength: "UPDATE"}).Raw(sql.RecursiveSQL, pageID).Scan(&topologics) if result.Error != nil { logger.Error("query circuit diagram topologic info by pageID failed", zap.Int64("pageID", pageID), zap.Error(result.Error)) return nil, result.Error @@ -38,29 +28,6 @@ func QueryTopologicByPageID(ctx context.Context, logger *zap.Logger, pageID int6 return topologics, nil } -// InitCircuitDiagramTopologic return circuit diagram topologic info from postgres -func InitCircuitDiagramTopologic(pageID int64, topologicNodes []orm.Topologic) error { - var rootVertex uuid.UUID - - for _, node := range topologicNodes { - if node.UUIDFrom.IsNil() { - rootVertex = node.UUIDTo - break - } - } - - topologicSet := diagram.NewGraph(rootVertex) - - for _, node := range topologicNodes { - if node.UUIDFrom.IsNil() { - continue - } - topologicSet.AddEdge(node.UUIDFrom, node.UUIDTo) - } - diagram.StoreGraphMap(pageID, topologicSet) - return nil -} - // QueryTopologicFromDB return the result of query topologic info from postgresDB func QueryTopologicFromDB(ctx context.Context, logger *zap.Logger, gridID, zoneID, stationID int64) error { allPages, err := QueryAllPages(ctx, logger, gridID, zoneID, stationID) @@ -83,3 +50,27 @@ func QueryTopologicFromDB(ctx context.Context, logger *zap.Logger, gridID, zoneI } return nil } + +// InitCircuitDiagramTopologic return circuit diagram topologic info from postgres +func InitCircuitDiagramTopologic(pageID int64, topologicNodes []orm.Topologic) error { + var rootVertex uuid.UUID + + for _, node := range topologicNodes { + if node.UUIDFrom.IsNil() { + rootVertex = node.UUIDTo + break + } + } + + topologicSet := diagram.NewGraph(rootVertex) + + for _, node := range topologicNodes { + if node.UUIDFrom.IsNil() { + continue + } + // TODO 增加对 node.flag值的判断 + topologicSet.AddEdge(node.UUIDFrom, node.UUIDTo) + } + diagram.StoreGraphMap(pageID, topologicSet) + return nil +} diff --git a/database/update_topologic.go b/database/update_topologic.go new file mode 100644 index 0000000..d00c842 --- /dev/null +++ b/database/update_topologic.go @@ -0,0 +1,47 @@ +// Package database define database operation functions +package database + +import ( + "context" + "fmt" + "time" + + "modelRT/constant" + "modelRT/network" + "modelRT/orm" + + "gorm.io/gorm" +) + +// UpdateTopologicIntoDB define update topologic info of the circuit diagram query by pageID and topologic info +func UpdateTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, changeInfo network.TopologicUUIDChangeInfos) error { + var result *gorm.DB + + cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() + + switch changeInfo.ChangeType { + case constant.UUIDFromChangeType: + result = tx.WithContext(cancelCtx).Model(orm.Topologic{}).Where("page_id = ? nad uuid_from = ? and uuid_to = ?", pageID, changeInfo.OldUUIDFrom, changeInfo.OldUUIDTo).Updates(orm.Topologic{UUIDFrom: changeInfo.NewUUIDFrom}) + case constant.UUIDToChangeType: + result = tx.WithContext(cancelCtx).Model(orm.Topologic{}).Where("page_id = ? nad uuid_from = ? and uuid_to = ?", pageID, changeInfo.OldUUIDFrom, changeInfo.OldUUIDTo).Updates(orm.Topologic{UUIDTo: changeInfo.NewUUIDTo}) + if result.Error != nil || result.RowsAffected == 0 { + return fmt.Errorf("insert new topologic link failed:%w", result.Error) + } + case constant.UUIDAddChangeType: + topologic := orm.Topologic{ + PageID: pageID, + Flag: changeInfo.Flag, + UUIDFrom: changeInfo.NewUUIDFrom, + UUIDTo: changeInfo.OldUUIDFrom, + Comment: changeInfo.Comment, + } + result = tx.WithContext(cancelCtx).Create(&topologic) + } + + // TODO 测试下RowsAffected==0时候,result.Error是否有值,是否会带来空指针问题 + if result.Error != nil || result.RowsAffected == 0 { + return fmt.Errorf("insert or update topologic link failed:%w", result.Error) + } + return nil +} diff --git a/diagram/graph.go b/diagram/graph.go index 7f43735..241047d 100644 --- a/diagram/graph.go +++ b/diagram/graph.go @@ -5,6 +5,9 @@ import ( "fmt" "sync" + "modelRT/constant" + "modelRT/network" + "github.com/gofrs/uuid" ) @@ -44,7 +47,8 @@ func (g *Graph) CreateBackLink(vertex string) { } } -// AddEdge adds an edge between two VerticeLinks +// AddEdge adds an edge between two verticeLinks +// TODO 在添加拓扑信息时是否考虑过滤重复节点 func (g *Graph) AddEdge(from, to uuid.UUID) { g.Lock() defer g.Unlock() @@ -88,7 +92,7 @@ func (g *Graph) DelNode(vertex string) error { return nil } -// DelEdge delete an edge between two VerticeLinks +// DelEdge delete an edge between two verticeLinks func (g *Graph) DelEdge(from, to uuid.UUID) error { g.Lock() defer g.Unlock() @@ -134,3 +138,14 @@ func (g *Graph) PrintGraph() { fmt.Printf("%s -> %v\n", vertex, edges) } } + +// UpdateEdge update edge link info between two verticeLinks +func (g *Graph) UpdateEdge(changeInfo network.TopologicUUIDChangeInfos) error { + if changeInfo.ChangeType == constant.UUIDFromChangeType || changeInfo.ChangeType == constant.UUIDToChangeType { + g.DelEdge(changeInfo.OldUUIDFrom, changeInfo.OldUUIDTo) + g.AddEdge(changeInfo.NewUUIDFrom, changeInfo.NewUUIDTo) + } else { + g.AddEdge(changeInfo.NewUUIDFrom, changeInfo.NewUUIDTo) + } + return nil +} diff --git a/example/circuit_diagram_update.json b/example/circuit_diagram_update.json new file mode 100644 index 0000000..ed05926 --- /dev/null +++ b/example/circuit_diagram_update.json @@ -0,0 +1,38 @@ +{ + "page_id":1, + "topologics":[ + { + "change_type":1, + "old_uuid_from":"12311-111", + "old_uuid_to":"12311-113", + "new_uuid_from":"12311-111", + "new_uuid_to":"12311-114" + }, + { + "change_type":2, + "old_uuid_from":"12311-111", + "old_uuid_to":"12311-113", + "new_uuid_from":"12311-112", + "new_uuid_to":"12311-113" + }, + { + "change_type":3, + "old_uuid_from":"", + "old_uuid_to":"", + "new_uuid_from":"12311-115", + "new_uuid_to":"12311-116" + } + ], + "component_infos":[ + { + "uuid":"12311-113", + "component_type":1, + "params":"" + }, + { + "uuid":"12311-114", + "component_type":1, + "params":"" + } + ] +} diff --git a/handler/circuit_diagram_update.go b/handler/circuit_diagram_update.go index 07b1f55..38f720f 100644 --- a/handler/circuit_diagram_update.go +++ b/handler/circuit_diagram_update.go @@ -12,7 +12,6 @@ import ( "modelRT/network" "github.com/gin-gonic/gin" - "github.com/gofrs/uuid" jsoniter "github.com/json-iterator/go" cmap "github.com/orcaman/concurrent-map/v2" "go.uber.org/zap" @@ -49,33 +48,49 @@ func CircuitDiagramUpdateHandler(c *gin.Context) { } // TODO 开启事务保证数据一致性 - for _, newTopologicLink := range request.TopologicLinks { - fromUUID, err1 := uuid.FromString(newTopologicLink.FromUUID) - toUUID, err2 := uuid.FromString(newTopologicLink.ToUUID) - if err1 != nil || err2 != nil { - var err error - if err1 != nil { - err = fmt.Errorf("format from_uuid failed:%w", err1) - } - - if err2 != nil { - err = fmt.Errorf("%s,format to_uuid failed:%w", err.Error(), err2) - } - + for _, topologicLink := range request.TopologicLinks { + changeInfo, err := network.ParseUUID(topologicLink) + if err != nil { logger.Error("format uuid from string failed", zap.Error(err)) header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()} resp := network.BasicResponse{ ResponseHeader: header, PayLoad: map[string]interface{}{ - "from_uuid": newTopologicLink.FromUUID, - "to_uuid": newTopologicLink.ToUUID, + "topologic_info": topologicLink, + }, + } + c.JSON(http.StatusOK, resp) + return + } + err = graph.UpdateEdge(changeInfo) + if err != nil { + logger.Error("update topologic info failed", zap.Any("topologic_info", topologicLink), zap.Error(err)) + header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()} + resp := network.BasicResponse{ + ResponseHeader: header, + PayLoad: map[string]interface{}{ + "topologic_info": topologicLink, + }, + } + c.JSON(http.StatusOK, resp) + return + } + + // TODO 使用事务tx 代替 pg 全局 DB + err = database.UpdateTopologicIntoDB(c, pgClient, request.PageID, changeInfo) + if err != nil { + // TODO 修改报错内容 + logger.Error("update topologic info into DB failed", zap.Any("topologic_info", topologicLink), zap.Error(err)) + header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()} + resp := network.BasicResponse{ + ResponseHeader: header, + PayLoad: map[string]interface{}{ + "topologic_info": topologicLink, }, } c.JSON(http.StatusOK, resp) return } - graph.AddEdge(fromUUID, toUUID) - // TODO 同步更新到 pg 数据表中 } for _, componentInfo := range request.ComponentInfos { diff --git a/network/request.go b/network/request.go index 5e052d4..a376b0b 100644 --- a/network/request.go +++ b/network/request.go @@ -1,10 +1,34 @@ // Package network define struct of network operation package network +import ( + "fmt" + + "modelRT/constant" + + "github.com/gofrs/uuid" +) + // TopologicChangeInfo defines circuit diagram topologic change info type TopologicChangeInfo struct { - FromUUID string `json:"from_uuid"` - ToUUID string `json:"to_uuid"` + ChangeType int `json:"change_type"` + Flag int `json:"flag"` + OldUUIDFrom string `json:"old_uuid_from"` + OldUUIDTo string `json:"old_uuid_to"` + NewUUIDFrom string `json:"new_uuid_from"` + NewUUIDTo string `json:"new_uuid_to"` + Comment string `json:"comment"` +} + +// TopologicUUIDChangeInfos defines circuit diagram topologic uuid change info +type TopologicUUIDChangeInfos struct { + ChangeType int `json:"change_type"` + Flag int `json:"flag"` + OldUUIDFrom uuid.UUID `json:"old_uuid_from"` + OldUUIDTo uuid.UUID `json:"old_uuid_to"` + NewUUIDFrom uuid.UUID `json:"new_uuid_from"` + NewUUIDTo uuid.UUID `json:"new_uuid_to"` + Comment string `json:"comment"` } // ComponentInfo defines circuit diagram component params info @@ -20,3 +44,86 @@ type CircuitDiagramUpdateRequest struct { TopologicLinks []TopologicChangeInfo `json:"topologics"` ComponentInfos []ComponentInfo `json:"component_infos"` } + +func ParseUUID(info TopologicChangeInfo) (TopologicUUIDChangeInfos, error) { + var UUIDChangeInfo TopologicUUIDChangeInfos + fmt.Println(info.ChangeType) + switch info.ChangeType { + case constant.UUIDFromChangeType: + if info.NewUUIDFrom == info.OldUUIDFrom { + return UUIDChangeInfo, fmt.Errorf("topologic change data check failed:%w", constant.ErrUUIDFromCheckT1) + } + if info.NewUUIDTo != info.OldUUIDTo { + return UUIDChangeInfo, fmt.Errorf("topologic change data check failed:%w", constant.ErrUUIDToCheckT1) + } + + oldUUIDFrom, err := uuid.FromString(info.OldUUIDFrom) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,old uuid_from value:%s", info.OldUUIDFrom) + } + UUIDChangeInfo.OldUUIDFrom = oldUUIDFrom + + newUUIDFrom, err := uuid.FromString(info.NewUUIDFrom) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,new uuid_from value:%s", info.NewUUIDFrom) + } + UUIDChangeInfo.NewUUIDFrom = newUUIDFrom + + OldUUIDTo, err := uuid.FromString(info.OldUUIDTo) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,old uuid_to value:%s", info.OldUUIDTo) + } + UUIDChangeInfo.OldUUIDTo = OldUUIDTo + UUIDChangeInfo.NewUUIDTo = OldUUIDTo + case constant.UUIDToChangeType: + if info.NewUUIDFrom != info.OldUUIDFrom { + return UUIDChangeInfo, fmt.Errorf("topologic change data check failed:%w", constant.ErrUUIDFromCheckT2) + } + if info.NewUUIDTo == info.OldUUIDTo { + return UUIDChangeInfo, fmt.Errorf("topologic change data check failed:%w", constant.ErrUUIDToCheckT2) + } + + oldUUIDFrom, err := uuid.FromString(info.OldUUIDFrom) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,old uuid_from value:%s", info.OldUUIDFrom) + } + UUIDChangeInfo.OldUUIDFrom = oldUUIDFrom + UUIDChangeInfo.NewUUIDFrom = oldUUIDFrom + + OldUUIDTo, err := uuid.FromString(info.OldUUIDTo) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,old uuid_to value:%s", info.OldUUIDTo) + } + UUIDChangeInfo.OldUUIDTo = OldUUIDTo + + newUUIDTo, err := uuid.FromString(info.NewUUIDTo) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,new uuid_to value:%s", info.NewUUIDTo) + } + UUIDChangeInfo.NewUUIDTo = newUUIDTo + case constant.UUIDAddChangeType: + if info.OldUUIDFrom != "" { + return UUIDChangeInfo, fmt.Errorf("topologic change data check failed:%w", constant.ErrUUIDFromCheckT3) + } + if info.OldUUIDTo != "" { + return UUIDChangeInfo, fmt.Errorf("topologic change data check failed:%w", constant.ErrUUIDToCheckT3) + } + + newUUIDFrom, err := uuid.FromString(info.NewUUIDFrom) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,new uuid_from value:%s", info.NewUUIDFrom) + } + UUIDChangeInfo.NewUUIDFrom = newUUIDFrom + + newUUIDTo, err := uuid.FromString(info.NewUUIDTo) + if err != nil { + return UUIDChangeInfo, fmt.Errorf("convert data from string type to uuid type failed,new uuid_to value:%s", info.NewUUIDTo) + } + UUIDChangeInfo.NewUUIDTo = newUUIDTo + default: + return UUIDChangeInfo, constant.ErrUUIDChangeType + } + UUIDChangeInfo.Flag = info.Flag + UUIDChangeInfo.Comment = info.Comment + return UUIDChangeInfo, nil +} diff --git a/sql/topologic.go b/sql/topologic.go new file mode 100644 index 0000000..635a6ac --- /dev/null +++ b/sql/topologic.go @@ -0,0 +1,14 @@ +// Package sql define database sql statement +package sql + +// RecursiveSQL define Topologic table recursive query statement +var RecursiveSQL = `WITH RECURSIVE recursive_tree as ( + SELECT uuid_from,uuid_to,page_id,flag + FROM "Topologic" + WHERE uuid_from is null and page_id = ? + 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;`