optimize code of component attrbute update api
This commit is contained in:
parent
f47e278f85
commit
60eab0675e
|
|
@ -4,9 +4,10 @@ package database
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"modelRT/orm"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"modelRT/orm"
|
||||||
|
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
|
|
@ -54,6 +55,23 @@ func QueryComponentByUUID(ctx context.Context, tx *gorm.DB, uuid uuid.UUID) (orm
|
||||||
return component, nil
|
return component, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryComponentByCompTag return the result of query circuit diagram component info by component tag from postgresDB
|
||||||
|
func QueryComponentByCompTag(ctx context.Context, tx *gorm.DB, tag string) (orm.Component, error) {
|
||||||
|
var component orm.Component
|
||||||
|
// ctx超时判断
|
||||||
|
cancelCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
result := tx.WithContext(cancelCtx).
|
||||||
|
Where("tag = ?", tag).
|
||||||
|
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||||
|
First(&component)
|
||||||
|
|
||||||
|
if result.Error != nil {
|
||||||
|
return orm.Component{}, result.Error
|
||||||
|
}
|
||||||
|
return component, nil
|
||||||
|
}
|
||||||
|
|
||||||
// QueryComponentByPageID return the result of query circuit diagram component info by page id from postgresDB
|
// QueryComponentByPageID return the result of query circuit diagram component info by page id from postgresDB
|
||||||
func QueryComponentByPageID(ctx context.Context, tx *gorm.DB, uuid uuid.UUID) (orm.Component, error) {
|
func QueryComponentByPageID(ctx context.Context, tx *gorm.DB, uuid uuid.UUID) (orm.Component, error) {
|
||||||
var component orm.Component
|
var component orm.Component
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"modelRT/logger"
|
"modelRT/logger"
|
||||||
|
|
@ -27,3 +29,50 @@ func QueryArrtibuteRecordByUUID(ctx context.Context, tx *gorm.DB, gridID, zoneID
|
||||||
}
|
}
|
||||||
return pages, nil
|
return pages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetProjectNameByTagAndGroupName 根据 tag 和 meta_model 获取项目名称
|
||||||
|
func GetProjectNameByTagAndGroupName(db *gorm.DB, tag string, groupName string) (string, error) {
|
||||||
|
var project orm.ProjectManager
|
||||||
|
|
||||||
|
// 使用 Select 只提取 name 字段,提高查询效率
|
||||||
|
// 使用 Where 进行多列条件过滤
|
||||||
|
err := db.Select("name").
|
||||||
|
Where("tag = ? AND meta_model = ?", tag, groupName).
|
||||||
|
First(&project).Error
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return "", fmt.Errorf("project not found with tag: %s and model: %s", tag, groupName)
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return project.Name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BatchGetProjectNames define func to batch retrieve name based on multiple tags and metaModel
|
||||||
|
func BatchGetProjectNames(db *gorm.DB, identifiers []orm.ProjectIdentifier) (map[orm.ProjectIdentifier]string, error) {
|
||||||
|
if len(identifiers) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var projects []orm.ProjectManager
|
||||||
|
queryArgs := make([][]any, len(identifiers))
|
||||||
|
for i, id := range identifiers {
|
||||||
|
queryArgs[i] = []any{id.Tag, id.MetaModel}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.Select("tag", "meta_model", "name").
|
||||||
|
Where("(tag, meta_model) IN ?", queryArgs).
|
||||||
|
Find(&projects).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resultMap := make(map[orm.ProjectIdentifier]string)
|
||||||
|
for _, p := range projects {
|
||||||
|
key := orm.ProjectIdentifier{Tag: p.Tag, MetaModel: p.MetaModel}
|
||||||
|
resultMap[key] = p.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMap, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,20 @@ package handler
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"modelRT/database"
|
"modelRT/database"
|
||||||
"modelRT/diagram"
|
"modelRT/diagram"
|
||||||
"modelRT/logger"
|
"modelRT/logger"
|
||||||
"modelRT/network"
|
"modelRT/network"
|
||||||
|
"modelRT/orm"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gofrs/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ComponentAttributeUpdateHandler define circuit diagram component attribute value update process API
|
// ComponentAttributeUpdateHandler define circuit diagram component attribute value update process API
|
||||||
func ComponentAttributeUpdateHandler(c *gin.Context) {
|
func ComponentAttributeUpdateHandler(c *gin.Context) {
|
||||||
pgClient := database.GetPostgresDBClient()
|
pgClient := database.GetPostgresDBClient()
|
||||||
|
|
||||||
var request network.ComponentAttributeUpdateInfo
|
var request network.ComponentAttributeUpdateInfo
|
||||||
if err := c.ShouldBindJSON(&request); err != nil {
|
if err := c.ShouldBindJSON(&request); err != nil {
|
||||||
logger.Error(c, "unmarshal circuit diagram component attribute update info failed", "error", err)
|
logger.Error(c, "unmarshal circuit diagram component attribute update info failed", "error", err)
|
||||||
|
|
@ -30,28 +30,57 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
componentUUID := uuid.FromStringOrNil(request.UUIDStr)
|
identifiers := make([]orm.ProjectIdentifier, len(request.AttributeConfigs))
|
||||||
if componentUUID == uuid.Nil {
|
attriModifyConfs := make([]attributeModifyConfig, len(request.AttributeConfigs))
|
||||||
err := fmt.Errorf("convert component uuid failed")
|
|
||||||
logger.Error(c, "convert component uuid type from string to uuid failed", "error", err)
|
|
||||||
|
|
||||||
resp := network.FailureResponse{
|
var attributeComponentTag string
|
||||||
Code: http.StatusBadRequest,
|
for index, attribute := range request.AttributeConfigs {
|
||||||
Msg: err.Error(),
|
slices := strings.Split(attribute.AttributeToken, ".")
|
||||||
|
if len(slices) < 7 {
|
||||||
|
err := fmt.Errorf("index:%d's attribute token format is invalid: %s", index, attribute.AttributeToken)
|
||||||
|
logger.Error(c, "parse attribute token failed", "error", err)
|
||||||
|
resp := network.FailureResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Msg: "parse attribute token failed",
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
componentTag := slices[4]
|
||||||
|
extendName := slices[5]
|
||||||
|
if index == 0 {
|
||||||
|
attributeComponentTag = componentTag
|
||||||
|
}
|
||||||
|
if componentTag != attributeComponentTag {
|
||||||
|
err := fmt.Errorf("batch update across multiple components is not allowed: [%s] vs [%s]", attributeComponentTag, componentTag)
|
||||||
|
logger.Error(c, "attribute consistency check failed", "error", err)
|
||||||
|
|
||||||
|
resp := network.FailureResponse{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Msg: "all attributes must belong to the same component tag",
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
attriModifyConfs[index] = attributeModifyConfig{
|
||||||
|
attributeExtendName: extendName,
|
||||||
|
attributeName: slices[6],
|
||||||
|
attributeOldVal: attribute.AttributeOldVal,
|
||||||
|
attributeNewVal: attribute.AttributeNewVal,
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, resp)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// open transaction
|
// open transaction
|
||||||
tx := pgClient.Begin()
|
tx := pgClient.Begin()
|
||||||
|
|
||||||
componentInfo, err := database.QueryComponentByUUID(c, tx, componentUUID)
|
// query component base info
|
||||||
|
compInfo, err := database.QueryComponentByCompTag(c, tx, attributeComponentTag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(c, "query component info by uuid from postgres failed failed", "error", err)
|
logger.Error(c, "query component info by component tag from postgres failed", "error", err)
|
||||||
|
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
|
|
||||||
resp := network.FailureResponse{
|
resp := network.FailureResponse{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
Msg: err.Error(),
|
Msg: err.Error(),
|
||||||
|
|
@ -60,23 +89,76 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 从 project_manager 表进行查询,然后更新对应表的数据的值
|
for i := range attriModifyConfs {
|
||||||
|
identifiers[i] = orm.ProjectIdentifier{
|
||||||
|
Tag: compInfo.ModelName,
|
||||||
|
MetaModel: attriModifyConfs[i].attributeExtendName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO 更新 redis 中的缓存的数值
|
// batch retrieve table name mappings
|
||||||
componentAttributeKey := fmt.Sprintf("%s_%s", componentInfo.Tag, request.AttributeConfigs[0].AttributeExtendType)
|
tableNameMap, err := database.BatchGetProjectNames(tx, identifiers)
|
||||||
// attributeSet.CompTag, colParams.AttributeType
|
if err != nil {
|
||||||
attributeHSet := diagram.NewRedisHash(c, componentAttributeKey, 5000, false)
|
tx.Rollback()
|
||||||
attributeHSet.SetRedisHashByKV("", nil)
|
// TODO 增加错误处理日志
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
redisUpdateMap := make(map[string][]cacheItem)
|
||||||
|
|
||||||
|
for _, mod := range attriModifyConfs {
|
||||||
|
id := orm.ProjectIdentifier{Tag: compInfo.ModelName, MetaModel: mod.attributeExtendName}
|
||||||
|
tableName, exists := tableNameMap[id]
|
||||||
|
if !exists {
|
||||||
|
// TODO 增加记录警告
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
updateErr := tx.Table(tableName).
|
||||||
|
Where(fmt.Sprintf("%s = ?", mod.attributeName), mod.attributeOldVal).
|
||||||
|
Updates(map[string]any{mod.attributeName: mod.attributeNewVal}).Error
|
||||||
|
|
||||||
|
if updateErr != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
// TODO 增加错误处理日志
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init cache item
|
||||||
|
cacheKey := fmt.Sprintf("%s_%s", attributeComponentTag, mod.attributeExtendName)
|
||||||
|
redisUpdateMap[cacheKey] = append(redisUpdateMap[cacheKey], cacheItem{mod.attributeName, mod.attributeNewVal})
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, items := range redisUpdateMap {
|
||||||
|
hset := diagram.NewRedisHash(c, key, 5000, false)
|
||||||
|
for _, item := range items {
|
||||||
|
_ = hset.SetRedisHashByKV(item.name, item.newVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// commit transaction
|
// commit transaction
|
||||||
tx.Commit()
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
resp := network.SuccessResponse{
|
resp := network.SuccessResponse{
|
||||||
Code: http.StatusOK,
|
Code: http.StatusOK,
|
||||||
Msg: "success",
|
Msg: "success",
|
||||||
Payload: map[string]interface{}{
|
Payload: map[string]interface{}{
|
||||||
"uuid": request.UUIDStr,
|
"uuid": "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, resp)
|
c.JSON(http.StatusOK, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type attributeModifyConfig struct {
|
||||||
|
attributeExtendName string
|
||||||
|
attributeName string
|
||||||
|
attributeOldVal string
|
||||||
|
attributeNewVal string
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheItem struct {
|
||||||
|
name string
|
||||||
|
newVal string
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,6 @@ func (l *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql
|
||||||
if duration > l.SlowThreshold.Milliseconds() {
|
if duration > l.SlowThreshold.Milliseconds() {
|
||||||
Warn(ctx, "SQL SLOW", "sql", sql, "rows", rows, "dur(ms)", duration)
|
Warn(ctx, "SQL SLOW", "sql", sql, "rows", rows, "dur(ms)", duration)
|
||||||
} else {
|
} else {
|
||||||
Debug(ctx, "SQL DEBUG", "sql", sql, "rows", rows, "dur(ms)", duration)
|
Info(ctx, "SQL INFO", "sql", sql, "rows", rows, "dur(ms)", duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,12 @@ package network
|
||||||
|
|
||||||
// ComponentAttributeUpdateInfo defines circuit diagram component attribute params info
|
// ComponentAttributeUpdateInfo defines circuit diagram component attribute params info
|
||||||
type ComponentAttributeUpdateInfo struct {
|
type ComponentAttributeUpdateInfo struct {
|
||||||
UUIDStr string `json:"uuid"`
|
|
||||||
AttributeConfigs []ComponentAttributeConfig `json:"attributes"`
|
AttributeConfigs []ComponentAttributeConfig `json:"attributes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComponentAttributeConfig defines request params of circuit diagram component attribute update config
|
// ComponentAttributeConfig defines request params of circuit diagram component attribute update config
|
||||||
type ComponentAttributeConfig struct {
|
type ComponentAttributeConfig struct {
|
||||||
AttributeExtendType string `json:"attribute_extend_type"`
|
AttributeToken string `json:"token"`
|
||||||
AttributeName string `json:"attribute_name"`
|
AttributeOldVal string `json:"attribute_old_val"`
|
||||||
AttributeVal string `json:"attribute_val"`
|
AttributeNewVal string `json:"attribute_new_val"`
|
||||||
AttributeFieldType string `json:"attribute_field_type"`
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,9 @@ type ProjectManager struct {
|
||||||
func (p *ProjectManager) TableName() string {
|
func (p *ProjectManager) TableName() string {
|
||||||
return "project_manager"
|
return "project_manager"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProjectIdentifier define struct to encapsulate query parameter pairs
|
||||||
|
type ProjectIdentifier struct {
|
||||||
|
Tag string
|
||||||
|
MetaModel string
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue