modelRT/handler/component_attribute_update.go

215 lines
6.3 KiB
Go

// 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)
renderRespFailure(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,
attributeExtendType: slices[5],
attributeName: slices[6],
attributeOldVal: attribute.AttributeOldVal,
attributeNewVal: attribute.AttributeNewVal,
})
}
// open transaction
tx := pgClient.WithContext(c).Begin()
if tx.Error != nil {
logger.Error(c, "begin postgres transaction failed", "error", tx.Error)
renderRespFailure(c, constants.RespCodeServerError, "begin postgres transaction failed", nil)
return
}
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()
payload := genUpdateRespPayload(updateResults, request.AttributeConfigs)
renderRespFailure(c, constants.RespCodeFailed, "query component metadata failed", payload)
return
}
identifiers := make([]orm.ProjectIdentifier, len(attriModifyConfs))
for i, mod := range attriModifyConfs {
identifiers[i] = orm.ProjectIdentifier{
Token: mod.attributeToken,
Tag: compInfo.ModelName,
GroupName: mod.attributeExtendType,
}
}
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)
}
}
payload := genUpdateRespPayload(updateResults, request.AttributeConfigs)
renderRespFailure(c, constants.RespCodeFailed, "batch retrieve table names failed", payload)
return
}
redisUpdateMap := make(map[string][]cacheUpdateItem)
for _, mod := range attriModifyConfs {
id := orm.ProjectIdentifier{Tag: compInfo.ModelName, GroupName: mod.attributeExtendType}
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.attributeExtendType)
redisUpdateMap[cacheKey] = append(redisUpdateMap[cacheKey],
cacheUpdateItem{
token: mod.attributeToken,
name: mod.attributeName,
newVal: mod.attributeNewVal,
})
}
// commit transaction
if err := tx.Commit().Error; err != nil {
renderRespFailure(c, constants.RespCodeServerError, "transaction commit failed", nil)
return
}
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)
}
}
}
}
payload := genUpdateRespPayload(updateResults, request.AttributeConfigs)
if len(updateResults) > 0 {
renderRespFailure(c, constants.RespCodeFailed, "process completed with partial failures", payload)
return
}
renderRespSuccess(c, constants.RespCodeSuccess, "process completed successfully", payload)
}
type attributeModifyConfig struct {
attributeToken string
attributeExtendType string
attributeName string
attributeOldVal string
attributeNewVal string
}
type cacheUpdateItem struct {
token string
name string
newVal string
}
type attributeUpdateResult struct {
Token string `json:"token"`
Code int `json:"code"`
Msg string `json:"msg"`
}
func genUpdateRespPayload(updateResults map[string]*errcode.AppError, originalRequests []network.ComponentAttributeConfig) map[string]any {
attributes := make([]attributeUpdateResult, 0, len(originalRequests))
for _, req := range originalRequests {
token := req.AttributeToken
if appErr, exists := updateResults[token]; exists {
attributes = append(attributes, attributeUpdateResult{
Token: token,
Code: appErr.Code(),
Msg: appErr.Msg(),
})
} else {
attributes = append(attributes, attributeUpdateResult{
Token: token,
Code: constants.CodeSuccess,
Msg: "token value update success",
})
}
}
payload := map[string]any{
"attributes": attributes,
}
return payload
}