optimize circuit diagram update handler and preliminary completion of circuit diagram delete handler code writing

This commit is contained in:
douxu 2024-12-04 15:57:11 +08:00
parent 214b194817
commit ebe86539d4
17 changed files with 383 additions and 41 deletions

View File

@ -1,7 +1,7 @@
postgres:
host: "localhost"
host: "192.168.2.156"
port: 5432
database: "model_rt"
database: "circuit_diagram"
user: "postgres"
password: "coslight"

View File

@ -5,6 +5,12 @@ import "errors"
// ErrUUIDChangeType define error of check uuid from value failed in uuid from change type
var ErrUUIDChangeType = errors.New("undefined uuid change type")
// ErrUpdateRowZero define error of update affected row zero
var ErrUpdateRowZero = errors.New("update affected rows is zero")
// ErrDeleteRowZero define error of delete affected row zero
var ErrDeleteRowZero = errors.New("delete affected rows is zero")
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")

View File

@ -0,0 +1,31 @@
// Package database define database operation functions
package database
import (
"context"
"fmt"
"time"
"modelRT/constant"
"modelRT/network"
"modelRT/orm"
"gorm.io/gorm"
)
// DeleteTopologicIntoDB define delete topologic info of the circuit diagram query by pageID and topologic info
func DeleteTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, delInfo network.TopologicUUIDDelInfos) error {
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
result := tx.Model(&orm.Topologic{}).WithContext(cancelCtx).Where("page_id = ? and uuid_from = ? and uuid_to = ?", pageID, delInfo.UUIDFrom, delInfo.UUIDTo).Delete(&orm.Topologic{})
if result.Error != nil || result.RowsAffected == 0 {
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check delete topologic where conditions", constant.ErrDeleteRowZero)
}
return fmt.Errorf("delete topologic link failed:%w", err)
}
return nil
}

View File

@ -27,9 +27,3 @@ func QueryAllPages(ctx context.Context, logger *zap.Logger, gridID, zoneID, stat
return pages, nil
}
// select "Page".id, "Page".station_id,"Station".zone_id,"Zone".grid_id,"Page".Name, "Page".status,"Page".context from "Page" inner join "Station" on "Station".id = "Page".station_id
// inner join "Zone" on "Zone".id = "Station".zone_id inner join "Grid" on "Grid".id = "Zone".grid_id
// where "Grid".id = 1 and "Zone".id=1 and "Station".id=1
// _globalPostgresClient.Model(&orm.Page{}).Clauses(clause.Locking{Strength: "UPDATE"}).Select(`"Page".id, "Page".Name, "Page".status,"Page".context`).Joins(`inner join "Station" on "Station".id = "Page".station_id`).Joins(`inner join "Zone" on "Zone".id = "Station".zone_id`).Joins(`inner join "Grid" on "Grid".id = "Zone".grid_id`).Where(`"Grid".id = ? and "Zone".id = ? and "Station".id = ?`, 1, 1, 1).Scan(&pages)

View File

@ -22,12 +22,9 @@ func UpdateTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, chang
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})
result = tx.WithContext(cancelCtx).Model(&orm.Topologic{}).Where("page_id = ? and 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)
}
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,
@ -38,10 +35,15 @@ func UpdateTopologicIntoDB(ctx context.Context, tx *gorm.DB, pageID int64, chang
}
result = tx.WithContext(cancelCtx).Create(&topologic)
}
// TODO 测试下RowsAffected==0时候result.Error是否有值,是否会带来空指针问题
// update 检查 result.RowsAffected 结果时,RowsAffected==0
// 可能存在 result.Error 为 nil 的情况,谨慎使用 result.Error.Error()
// 函数,避免造成空指针问题
if result.Error != nil || result.RowsAffected == 0 {
return fmt.Errorf("insert or update topologic link failed:%w", result.Error)
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check update topologic where conditions", constant.ErrUpdateRowZero)
}
return fmt.Errorf("insert or update topologic link failed:%w", err)
}
return nil
}

View File

@ -35,3 +35,9 @@ func StoreComponentMap(uuid string, componentInfo *cmap.ConcurrentMap[string, an
diagramsOverview.Store(uuid, componentInfo)
return
}
// DeleteComponentMap define func of delete circuit diagram data with global uuid
func DeleteComponentMap(uuid string) {
diagramsOverview.Delete(uuid)
return
}

View File

@ -33,3 +33,9 @@ func StoreGraphMap(pageID int64, graphInfo *Graph) {
diagramsOverview.Store(pageID, graphInfo)
return
}
// DeleteGraphMap define func of delete circuit diagram topologic data with pageID
func DeleteGraphMap(pageID int64) {
diagramsOverview.Delete(pageID)
return
}

View File

@ -0,0 +1,24 @@
{
"page_id":1,
"topologics":[
{
"uuid_from":"12311-111",
"uuid_to":"12311-114"
},
{
"uuid_from":"12311-115",
"uuid_to":"12311-116"
}
],
"component_infos":[
{
"uuid":"12311-114",
"component_type":1
},
{
"uuid":"12311-116",
"component_type":1
}
]
}

View File

@ -0,0 +1,202 @@
package handler
import (
"context"
"fmt"
"net/http"
"time"
"modelRT/constant"
"modelRT/database"
"modelRT/diagram"
"modelRT/log"
"modelRT/model"
"modelRT/network"
"modelRT/orm"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
"go.uber.org/zap"
)
// CircuitDiagramDeleteHandler define circuit diagram delete process API
func CircuitDiagramDeleteHandler(c *gin.Context) {
logger := log.GetLoggerInstance()
pgClient := database.GetPostgresDBClient()
var request network.CircuitDiagramDeleteRequest
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error("unmarshal circuit diagram del info failed", zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
}
c.JSON(http.StatusOK, resp)
return
}
graph, err := diagram.GetGraphMap(request.PageID)
if err != nil {
logger.Error("get topologic data from set by pageID failed", zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},
}
c.JSON(http.StatusOK, resp)
return
}
var topologicDelInfos []network.TopologicUUIDDelInfos
for _, topologicLink := range request.TopologicLinks {
var delInfo network.TopologicUUIDDelInfos
UUIDFrom, err1 := uuid.FromString(topologicLink.UUIDFrom)
UUIDTo, err2 := uuid.FromString(topologicLink.UUIDTo)
if err1 != nil || err2 != nil {
var err error
if err1 != nil && err2 == nil {
err = fmt.Errorf("convert uuid from string failed:%w", err1)
} else if err1 == nil && err2 != nil {
err = fmt.Errorf("convert uuid from string failed:%w", err2)
} else {
err = fmt.Errorf("convert uuid from string failed:%w:%w", err1, err2)
}
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{}{
"topologic_info": topologicLink,
},
}
c.JSON(http.StatusOK, resp)
return
}
delInfo.UUIDFrom = UUIDFrom
delInfo.UUIDTo = UUIDTo
topologicDelInfos = append(topologicDelInfos, delInfo)
}
// open transaction
tx := pgClient.Begin()
for _, topologicDelInfo := range topologicDelInfos {
err = database.DeleteTopologicIntoDB(c, tx, request.PageID, topologicDelInfo)
if err != nil {
tx.Rollback()
logger.Error("delete topologic info into DB failed", zap.Any("topologic_info", topologicDelInfo), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"topologic_info": topologicDelInfo,
},
}
c.JSON(http.StatusOK, resp)
return
}
err = graph.DelEdge(topologicDelInfo.UUIDFrom, topologicDelInfo.UUIDTo)
if err != nil {
tx.Rollback()
logger.Error("delete topologic info failed", zap.Any("topologic_info", topologicDelInfo), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"topologic_info": topologicDelInfo,
},
}
c.JSON(http.StatusOK, resp)
return
}
}
if len(graph.VerticeLinks) == 0 && len(graph.FreeVertexs) == 0 {
diagram.DeleteGraphMap(request.PageID)
}
for _, componentInfo := range request.ComponentInfos {
cancelCtx, cancel := context.WithTimeout(c, 5*time.Second)
defer cancel()
globalUUID, err := uuid.FromString(componentInfo.UUID)
if err != nil {
tx.Rollback()
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{}{
"uuid": componentInfo.UUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
component := &orm.Component{GlobalUUID: globalUUID}
result := tx.WithContext(cancelCtx).Delete(component)
if result.Error != nil || result.RowsAffected == 0 {
tx.Rollback()
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check uuid conditions", constant.ErrDeleteRowZero)
}
logger.Error("delete component info into postgresDB failed", zap.String("component_global_uuid", componentInfo.UUID), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
modelStruct := model.SelectModelByType(component.ComponentType)
modelStruct.SetUUID(globalUUID)
result = tx.WithContext(cancelCtx).Delete(modelStruct)
if result.Error != nil || result.RowsAffected == 0 {
tx.Rollback()
err := result.Error
if result.RowsAffected == 0 {
err = fmt.Errorf("%w:please check uuid conditions", constant.ErrDeleteRowZero)
}
msg := fmt.Sprintf("delete component info from table %s failed", modelStruct.ReturnTableName())
logger.Error(msg, zap.String("component_global_uuid", componentInfo.UUID), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"uuid": componentInfo.UUID,
},
}
c.JSON(http.StatusOK, resp)
return
}
diagram.DeleteComponentMap(componentInfo.UUID)
}
// commit transsction
tx.Commit()
resp := network.BasicResponse{
ResponseHeader: network.ResponseHeader{Status: http.StatusOK},
PayLoad: map[string]interface{}{
"page_id": request.PageID,
},
}
c.JSON(http.StatusOK, resp)
}

View File

@ -1,10 +1,10 @@
package handler
import (
"errors"
"fmt"
"net/http"
"modelRT/constant"
"modelRT/database"
"modelRT/diagram"
"modelRT/log"
@ -47,7 +47,7 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
return
}
// TODO 开启事务保证数据一致性
var topologicChangeInfos []network.TopologicUUIDChangeInfos
for _, topologicLink := range request.TopologicLinks {
changeInfo, err := network.ParseUUID(topologicLink)
if err != nil {
@ -62,30 +62,39 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
c.JSON(http.StatusOK, resp)
return
}
err = graph.UpdateEdge(changeInfo)
topologicChangeInfos = append(topologicChangeInfos, changeInfo)
}
// open transaction
tx := pgClient.Begin()
for _, topologicChangeInfo := range topologicChangeInfos {
err = database.UpdateTopologicIntoDB(c, tx, request.PageID, topologicChangeInfo)
if err != nil {
logger.Error("update topologic info failed", zap.Any("topologic_info", topologicLink), zap.Error(err))
tx.Rollback()
logger.Error("update topologic info into DB failed", zap.Any("topologic_info", topologicChangeInfo), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"topologic_info": topologicLink,
"topologic_info": topologicChangeInfo,
},
}
c.JSON(http.StatusOK, resp)
return
}
// TODO 使用事务tx 代替 pg 全局 DB
err = database.UpdateTopologicIntoDB(c, pgClient, request.PageID, changeInfo)
err = graph.UpdateEdge(topologicChangeInfo)
if err != nil {
// TODO 修改报错内容
logger.Error("update topologic info into DB failed", zap.Any("topologic_info", topologicLink), zap.Error(err))
tx.Rollback()
logger.Error("update topologic info failed", zap.Any("topologic_info", topologicChangeInfo), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
resp := network.BasicResponse{
ResponseHeader: header,
PayLoad: map[string]interface{}{
"topologic_info": topologicLink,
"topologic_info": topologicChangeInfo,
},
}
c.JSON(http.StatusOK, resp)
@ -96,6 +105,8 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
for _, componentInfo := range request.ComponentInfos {
parseStruct := model.SelectModelByType(componentInfo.ComponentType)
if parseStruct == nil {
tx.Rollback()
err := fmt.Errorf("can not get component model by model type %d", componentInfo.ComponentType)
logger.Error("get component model by model type failed", zap.String("uuid", componentInfo.UUID), zap.Int("component_type", componentInfo.ComponentType), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
@ -112,6 +123,8 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
err := jsoniter.Unmarshal([]byte(componentInfo.Params), parseStruct)
if err != nil {
tx.Rollback()
componentStructName := model.SelectModelNameByType(componentInfo.ComponentType)
logger.Error("unmarshal component info by component struct failed", zap.String("component_struct_name", componentStructName), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
@ -127,11 +140,13 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
return
}
result := pgClient.Where("uuid = ?", componentInfo.UUID).Save(parseStruct)
result := tx.Where("uuid = ?", componentInfo.UUID).Save(parseStruct)
if result.Error != nil || result.RowsAffected == 0 {
tx.Rollback()
err := result.Error
if result.RowsAffected == 0 {
err = errors.New("update component info by uuid failed,affected rows zero")
err = fmt.Errorf("%w:please check where conditions", constant.ErrUpdateRowZero)
}
logger.Error("store component info into postgresDB failed", zap.Any("component_params", parseStruct), zap.Error(err))
header := network.ResponseHeader{Status: http.StatusBadRequest, ErrMsg: err.Error()}
@ -164,6 +179,9 @@ func CircuitDiagramUpdateHandler(c *gin.Context) {
diagram.UpdateComponentMap(componentInfo.UUID, &parseMap)
}
// commit transsction
tx.Commit()
resp := network.BasicResponse{
ResponseHeader: network.ResponseHeader{Status: http.StatusOK},
PayLoad: map[string]interface{}{

View File

@ -84,7 +84,7 @@ func main() {
engine.GET("/model/diagram_load", handler.CircuitDiagramLoadHandler)
engine.POST("/model/diagram_create", nil)
engine.POST("/model/diagram_update", handler.CircuitDiagramUpdateHandler)
engine.POST("/model/diagram_delete", nil)
engine.POST("/model/diagram_delete", handler.CircuitDiagramDeleteHandler)
// start route with 8080 port
engine.Run(":8080")

10
model/basic_model.go Normal file
View File

@ -0,0 +1,10 @@
// Package model define model struct of model runtime service
package model
import "github.com/gofrs/uuid"
// BasicModelInterface define basic model type interface
type BasicModelInterface interface {
SetUUID(uuid uuid.UUID)
ReturnTableName() string
}

View File

@ -7,7 +7,7 @@ import (
)
// SelectModelByType define select the data structure for parsing based on the input model type
func SelectModelByType(modelType int) any {
func SelectModelByType(modelType int) BasicModelInterface {
if modelType == constant.BusbarType {
return &orm.BusbarSection{}
}

View File

@ -0,0 +1,29 @@
// Package network define struct of network operation
package network
import "github.com/gofrs/uuid"
// TopologicDelInfo defines circuit diagram topologic delete info
type TopologicDelInfo struct {
UUIDFrom string `json:"uuid_from"`
UUIDTo string `json:"uuid_to"`
}
// TopologicUUIDDelInfos defines circuit diagram topologic uuid delete info
type TopologicUUIDDelInfos struct {
UUIDFrom uuid.UUID `json:"uuid_from"`
UUIDTo uuid.UUID `json:"uuid_to"`
}
// ComponentDelInfo defines circuit diagram component delete index info
type ComponentDelInfo struct {
UUID string `json:"uuid"`
ComponentType int `json:"component_type"`
}
// CircuitDiagramDeleteRequest defines request params of circuit diagram delete api
type CircuitDiagramDeleteRequest struct {
PageID int64 `json:"page_id"`
TopologicLinks []TopologicDelInfo `json:"topologics"`
ComponentInfos []ComponentDelInfo `json:"component_infos"`
}

View File

@ -47,7 +47,7 @@ type CircuitDiagramUpdateRequest struct {
func ParseUUID(info TopologicChangeInfo) (TopologicUUIDChangeInfos, error) {
var UUIDChangeInfo TopologicUUIDChangeInfos
fmt.Println(info.ChangeType)
switch info.ChangeType {
case constant.UUIDFromChangeType:
if info.NewUUIDFrom == info.OldUUIDFrom {

View File

@ -5,16 +5,16 @@ import "github.com/gofrs/uuid"
type BusbarSection struct {
// 母线基本参数
Name string // 母线端名称,默认值BusX
BusbarNumber int // 母线编号,默认值1
UUID uuid.UUID
StandardVoltage float32 // 标准电压,单位kV,范围值在000.01~500.00
Desc string // 描述
IsService bool // 是否服役,值为运行/退出
Status string // 状态,值为现役/新建/计划/检修/库存可用/库存报废
PowerGridName string // 当前工程电网的顶层建模时的电网名称
RegionName string // 当前工程电网的顶层建模时的区域电网名称
FactoryStationName string // 当前工程电网的顶层建模时的厂站名称
Name string // 母线端名称,默认值BusX
BusbarNumber int // 母线编号,默认值1
UUID uuid.UUID `gorm:"column:uuid;primaryKey"`
StandardVoltage float32 // 标准电压,单位kV,范围值在000.01~500.00
Desc string // 描述
IsService bool // 是否服役,值为运行/退出
Status string // 状态,值为现役/新建/计划/检修/库存可用/库存报废
PowerGridName string // 当前工程电网的顶层建模时的电网名称
RegionName string // 当前工程电网的顶层建模时的区域电网名称
FactoryStationName string // 当前工程电网的顶层建模时的厂站名称
// 母线模型参数
VoltagePercentValue float32 // 以母线标称电压为基准的百分数,默认值1.00~200.00
VoltageCalculcatedValue float32 // 通过StandardVoltage与VoltagePercentValtage计算得出的电压值,默认值0.01~1000.00
@ -67,6 +67,17 @@ func (b *BusbarSection) TableName() string {
return "BusbarSection"
}
// SetUUID func implement BasicModelInterface interface
func (b *BusbarSection) SetUUID(uuid uuid.UUID) {
b.UUID = uuid
return
}
// ReturnTableName func implement BasicModelInterface interface
func (b *BusbarSection) ReturnTableName() string {
return "BusbarSection"
}
func NewBusbarSection(name string) (*BusbarSection, error) {
uuid, err := uuid.NewV4()
if err != nil {

View File

@ -12,3 +12,6 @@ var RecursiveSQL = `WITH RECURSIVE recursive_tree as (
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);