// 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 }