optimize code of component attrbute update api

This commit is contained in:
douxu 2026-01-08 17:34:44 +08:00
parent f47e278f85
commit 60eab0675e
6 changed files with 182 additions and 29 deletions

View File

@ -4,9 +4,10 @@ package database
import (
"context"
"fmt"
"modelRT/orm"
"time"
"modelRT/orm"
"github.com/gofrs/uuid"
"gorm.io/gorm"
"gorm.io/gorm/clause"
@ -54,6 +55,23 @@ func QueryComponentByUUID(ctx context.Context, tx *gorm.DB, uuid uuid.UUID) (orm
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
func QueryComponentByPageID(ctx context.Context, tx *gorm.DB, uuid uuid.UUID) (orm.Component, error) {
var component orm.Component

View File

@ -3,6 +3,8 @@ package database
import (
"context"
"errors"
"fmt"
"time"
"modelRT/logger"
@ -27,3 +29,50 @@ func QueryArrtibuteRecordByUUID(ctx context.Context, tx *gorm.DB, gridID, zoneID
}
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
}

View File

@ -4,20 +4,20 @@ package handler
import (
"fmt"
"net/http"
"strings"
"modelRT/database"
"modelRT/diagram"
"modelRT/logger"
"modelRT/network"
"modelRT/orm"
"github.com/gin-gonic/gin"
"github.com/gofrs/uuid"
)
// ComponentAttributeUpdateHandler define circuit diagram component attribute value update process API
func ComponentAttributeUpdateHandler(c *gin.Context) {
pgClient := database.GetPostgresDBClient()
var request network.ComponentAttributeUpdateInfo
if err := c.ShouldBindJSON(&request); err != nil {
logger.Error(c, "unmarshal circuit diagram component attribute update info failed", "error", err)
@ -30,28 +30,57 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
return
}
componentUUID := uuid.FromStringOrNil(request.UUIDStr)
if componentUUID == uuid.Nil {
err := fmt.Errorf("convert component uuid failed")
logger.Error(c, "convert component uuid type from string to uuid failed", "error", err)
identifiers := make([]orm.ProjectIdentifier, len(request.AttributeConfigs))
attriModifyConfs := make([]attributeModifyConfig, len(request.AttributeConfigs))
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
var attributeComponentTag string
for index, attribute := range request.AttributeConfigs {
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
tx := pgClient.Begin()
componentInfo, err := database.QueryComponentByUUID(c, tx, componentUUID)
// query component base info
compInfo, err := database.QueryComponentByCompTag(c, tx, attributeComponentTag)
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()
resp := network.FailureResponse{
Code: http.StatusBadRequest,
Msg: err.Error(),
@ -60,23 +89,76 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
return
}
// TODO 从 project_manager 表进行查询,然后更新对应表的数据的值
for i := range attriModifyConfs {
identifiers[i] = orm.ProjectIdentifier{
Tag: compInfo.ModelName,
MetaModel: attriModifyConfs[i].attributeExtendName,
}
}
// TODO 更新 redis 中的缓存的数值
componentAttributeKey := fmt.Sprintf("%s_%s", componentInfo.Tag, request.AttributeConfigs[0].AttributeExtendType)
// attributeSet.CompTag, colParams.AttributeType
attributeHSet := diagram.NewRedisHash(c, componentAttributeKey, 5000, false)
attributeHSet.SetRedisHashByKV("", nil)
// batch retrieve table name mappings
tableNameMap, err := database.BatchGetProjectNames(tx, identifiers)
if err != nil {
tx.Rollback()
// 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
tx.Commit()
if err := tx.Commit().Error; err != nil {
return
}
resp := network.SuccessResponse{
Code: http.StatusOK,
Msg: "success",
Payload: map[string]interface{}{
"uuid": request.UUIDStr,
"uuid": "",
},
}
c.JSON(http.StatusOK, resp)
}
type attributeModifyConfig struct {
attributeExtendName string
attributeName string
attributeOldVal string
attributeNewVal string
}
type cacheItem struct {
name string
newVal string
}

View File

@ -56,6 +56,6 @@ func (l *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql
if duration > l.SlowThreshold.Milliseconds() {
Warn(ctx, "SQL SLOW", "sql", sql, "rows", rows, "dur(ms)", duration)
} else {
Debug(ctx, "SQL DEBUG", "sql", sql, "rows", rows, "dur(ms)", duration)
Info(ctx, "SQL INFO", "sql", sql, "rows", rows, "dur(ms)", duration)
}
}

View File

@ -3,14 +3,12 @@ package network
// ComponentAttributeUpdateInfo defines circuit diagram component attribute params info
type ComponentAttributeUpdateInfo struct {
UUIDStr string `json:"uuid"`
AttributeConfigs []ComponentAttributeConfig `json:"attributes"`
}
// ComponentAttributeConfig defines request params of circuit diagram component attribute update config
type ComponentAttributeConfig struct {
AttributeExtendType string `json:"attribute_extend_type"`
AttributeName string `json:"attribute_name"`
AttributeVal string `json:"attribute_val"`
AttributeFieldType string `json:"attribute_field_type"`
AttributeToken string `json:"token"`
AttributeOldVal string `json:"attribute_old_val"`
AttributeNewVal string `json:"attribute_new_val"`
}

View File

@ -17,3 +17,9 @@ type ProjectManager struct {
func (p *ProjectManager) TableName() string {
return "project_manager"
}
// ProjectIdentifier define struct to encapsulate query parameter pairs
type ProjectIdentifier struct {
Tag string
MetaModel string
}