// Package handler provides HTTP handlers for various endpoints. package handler import ( "fmt" "net/http" "strings" "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 circuit diagram component attribute update info failed", "error", err) resp := network.FailureResponse{ Code: http.StatusBadRequest, Msg: err.Error(), } c.JSON(http.StatusOK, resp) return } identifiers := make([]orm.ProjectIdentifier, len(request.AttributeConfigs)) attriModifyConfs := make([]attributeModifyConfig, len(request.AttributeConfigs)) 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, } } // open transaction tx := pgClient.Begin() // query component base info compInfo, err := database.QueryComponentByCompTag(c, tx, attributeComponentTag) if err != nil { 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(), } c.JSON(http.StatusOK, resp) return } for i := range attriModifyConfs { identifiers[i] = orm.ProjectIdentifier{ Tag: compInfo.ModelName, MetaModel: attriModifyConfs[i].attributeExtendName, } } // 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 if err := tx.Commit().Error; err != nil { return } resp := network.SuccessResponse{ Code: http.StatusOK, Msg: "success", Payload: map[string]interface{}{ "uuid": "", }, } c.JSON(http.StatusOK, resp) } type attributeModifyConfig struct { attributeExtendName string attributeName string attributeOldVal string attributeNewVal string } type cacheItem struct { name string newVal string }