modelRT/handler/component_attribute_update.go

176 lines
5.1 KiB
Go
Raw Normal View History

// Package handler provides HTTP handlers for various endpoints.
package handler
import (
"fmt"
"strings"
"modelRT/common/errcode"
"modelRT/constants"
"modelRT/database"
"modelRT/diagram"
"modelRT/logger"
"modelRT/network"
"modelRT/orm"
"github.com/gin-gonic/gin"
)
// 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 request params failed", "error", err)
renderFailure(c, constants.RespCodeInvalidParams, err.Error(), nil)
return
}
updateResults := make(map[string]*errcode.AppError)
attriModifyConfs := make([]attributeModifyConfig, 0, len(request.AttributeConfigs))
var attributeComponentTag string
for index, attribute := range request.AttributeConfigs {
slices := strings.Split(attribute.AttributeToken, ".")
if len(slices) < 7 {
updateResults[attribute.AttributeToken] = errcode.ErrInvalidToken
continue
}
componentTag := slices[4]
if index == 0 {
attributeComponentTag = componentTag
} else if componentTag != attributeComponentTag {
updateResults[attribute.AttributeToken] = errcode.ErrCrossToken
continue
}
attriModifyConfs = append(attriModifyConfs, attributeModifyConfig{
attributeToken: attribute.AttributeToken,
attributeExtendName: slices[5],
attributeName: slices[6],
attributeOldVal: attribute.AttributeOldVal,
attributeNewVal: attribute.AttributeNewVal,
})
}
// open transaction
tx := pgClient.Begin()
compInfo, err := database.QueryComponentByCompTag(c, tx, attributeComponentTag)
if err != nil {
logger.Error(c, "query component info by component tag failed", "error", err, "tag", attributeComponentTag)
for _, attribute := range request.AttributeConfigs {
if _, exists := updateResults[attribute.AttributeToken]; !exists {
updateResults[attribute.AttributeToken] = errcode.ErrDBQueryFailed.WithCause(err)
}
}
tx.Rollback()
renderFailure(c, constants.RespCodeFailed, "query component metadata failed", updateResults)
return
}
identifiers := make([]orm.ProjectIdentifier, len(attriModifyConfs))
for i, mod := range attriModifyConfs {
identifiers[i] = orm.ProjectIdentifier{
Token: mod.attributeToken,
Tag: compInfo.ModelName,
MetaModel: mod.attributeExtendName,
}
}
tableNameMap, err := database.BatchGetProjectNames(tx, identifiers)
if err != nil {
tx.Rollback()
for _, id := range identifiers {
if _, exists := updateResults[id.Token]; !exists {
updateResults[id.Token] = errcode.ErrRetrieveFailed.WithCause(err)
}
}
renderFailure(c, constants.RespCodeFailed, "batch retrieve table names failed", updateResults)
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 {
updateResults[mod.attributeToken] = errcode.ErrFoundTargetFailed
continue
}
result := tx.Table(tableName).
Where(fmt.Sprintf("%s = ? AND global_uuid = ?", mod.attributeName), mod.attributeOldVal, compInfo.GlobalUUID).
Updates(map[string]any{mod.attributeName: mod.attributeNewVal})
if result.Error != nil {
updateResults[mod.attributeToken] = errcode.ErrDBUpdateFailed
continue
}
if result.RowsAffected == 0 {
updateResults[mod.attributeToken] = errcode.ErrDBzeroAffectedRows
continue
}
cacheKey := fmt.Sprintf("%s_%s", attributeComponentTag, mod.attributeExtendName)
redisUpdateMap[cacheKey] = append(redisUpdateMap[cacheKey],
cacheItem{
token: mod.attributeToken,
name: mod.attributeName,
newVal: mod.attributeNewVal,
})
}
// commit transaction
if err := tx.Commit().Error; err != nil {
renderFailure(c, constants.RespCodeServerError, "transaction commit failed", nil)
return
}
for key, items := range redisUpdateMap {
hset := diagram.NewRedisHash(c, key, 5000, false)
for _, item := range items {
_ = hset.SetRedisHashByKV(item.name, item.newVal)
}
}
for key, items := range redisUpdateMap {
hset := diagram.NewRedisHash(c, key, 5000, false)
fields := make(map[string]any, len(items))
for _, item := range items {
fields[item.name] = item.newVal
}
if err := hset.SetRedisHashByMap(fields); err != nil {
logger.Error(c, "batch sync redis failed", "hash_key", key, "error", err)
for _, item := range items {
if _, exists := updateResults[item.token]; exists {
updateResults[item.token] = errcode.ErrCacheSyncWarn.WithCause(err)
}
}
}
}
// TODO 通过循环最初的 request 填充剩余处理正确token的updateResults结果
renderRespSuccess(c, constants.RespCodeSuccess, "process completed", updateResults)
}
type attributeModifyConfig struct {
attributeToken string
attributeExtendName string
attributeName string
attributeOldVal string
attributeNewVal string
}
type cacheItem struct {
token string
name string
newVal string
}