optimize code of component attribute update api
This commit is contained in:
parent
60eab0675e
commit
d1495b7ab8
|
|
@ -1,3 +1,4 @@
|
|||
// Package errcode provides internal error definition and business error definition
|
||||
package errcode
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package errcode provides internal error definition and business error definition
|
||||
package errcode
|
||||
|
||||
import "errors"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// Package errcode provides internal error definition and business error definition
|
||||
package errcode
|
||||
|
||||
import (
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// Package errcode provides internal error definition and business error definition
|
||||
package errcode
|
||||
|
||||
var (
|
||||
ErrProcessSuccess = newError(20000, "token value update success")
|
||||
ErrInvalidToken = newError(40001, "invalid token format")
|
||||
ErrCrossToken = newError(40002, "cross-component update not allowed")
|
||||
ErrRetrieveFailed = newError(40003, "retrieve table mapping failed")
|
||||
ErrFoundTargetFailed = newError(40003, "found target table by token failed")
|
||||
ErrDBQueryFailed = newError(50001, "query postgres database data failed")
|
||||
ErrDBUpdateFailed = newError(50002, "update postgres database data failed")
|
||||
ErrDBzeroAffectedRows = newError(50002, "zero affected rows")
|
||||
ErrCacheSyncWarn = newError(60002, "postgres database updated, but cache sync failed")
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// CodeSuccess define constant to indicates that the API was successfully processed
|
||||
CodeSuccess = 20000
|
||||
// CodeInvalidParamFailed define constant to indicates request parameter parsing failed
|
||||
CodeInvalidParamFailed = 40001
|
||||
// CodeDBQueryFailed define constant to indicates database query operation failed
|
||||
CodeDBQueryFailed = 50001
|
||||
// CodeDBUpdateailed define constant to indicates database update operation failed
|
||||
CodeDBUpdateailed = 50002
|
||||
// CodeRedisQueryFailed define constant to indicates redis query operation failed
|
||||
CodeRedisQueryFailed = 60001
|
||||
// CodeRedisUpdateFailed define constant to indicates redis update operation failed
|
||||
CodeRedisUpdateFailed = 60002
|
||||
)
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// Package constants define constant variable
|
||||
package constants
|
||||
|
||||
const (
|
||||
// RespCodeSuccess define constant to indicates that the API was processed success
|
||||
RespCodeSuccess = 2000
|
||||
|
||||
// RespCodeFailed define constant to indicates that the API was processed failed
|
||||
RespCodeFailed = 3000
|
||||
|
||||
// RespCodeInvalidParams define constant to indicates that the request parameters failed to validate, parsing failed, or the action is invalid
|
||||
RespCodeInvalidParams = 4001
|
||||
|
||||
// RespCodeUnauthorized define constant to indicates insufficient permissions or an invalid ClientID
|
||||
RespCodeUnauthorized = 4002
|
||||
|
||||
// RespCodeServerError define constants to indicates a serious internal server error (such as database disconnection or code panic)
|
||||
RespCodeServerError = 5000
|
||||
)
|
||||
|
|
@ -13,6 +13,7 @@ const (
|
|||
)
|
||||
|
||||
// 定义状态常量
|
||||
// TODO 从4位格式修改为5位格式
|
||||
const (
|
||||
// SubSuccessCode define subscription success code
|
||||
SubSuccessCode = "1001"
|
||||
|
|
@ -3,9 +3,10 @@ package handler
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"modelRT/common/errcode"
|
||||
"modelRT/constants"
|
||||
"modelRT/database"
|
||||
"modelRT/diagram"
|
||||
"modelRT/logger"
|
||||
|
|
@ -20,113 +21,113 @@ 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)
|
||||
logger.Error(c, "unmarshal request params failed", "error", err)
|
||||
renderFailure(c, constants.RespCodeInvalidParams, err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
identifiers := make([]orm.ProjectIdentifier, len(request.AttributeConfigs))
|
||||
attriModifyConfs := make([]attributeModifyConfig, len(request.AttributeConfigs))
|
||||
|
||||
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 {
|
||||
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
|
||||
updateResults[attribute.AttributeToken] = errcode.ErrInvalidToken
|
||||
continue
|
||||
}
|
||||
|
||||
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
|
||||
} else if componentTag != attributeComponentTag {
|
||||
updateResults[attribute.AttributeToken] = errcode.ErrCrossToken
|
||||
continue
|
||||
}
|
||||
|
||||
attriModifyConfs[index] = attributeModifyConfig{
|
||||
attributeExtendName: extendName,
|
||||
attriModifyConfs = append(attriModifyConfs, attributeModifyConfig{
|
||||
attributeToken: attribute.AttributeToken,
|
||||
attributeExtendName: slices[5],
|
||||
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)
|
||||
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()
|
||||
resp := network.FailureResponse{
|
||||
Code: http.StatusBadRequest,
|
||||
Msg: err.Error(),
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
|
||||
renderFailure(c, constants.RespCodeFailed, "query component metadata failed", updateResults)
|
||||
return
|
||||
}
|
||||
|
||||
for i := range attriModifyConfs {
|
||||
identifiers := make([]orm.ProjectIdentifier, len(attriModifyConfs))
|
||||
for i, mod := range attriModifyConfs {
|
||||
identifiers[i] = orm.ProjectIdentifier{
|
||||
Token: mod.attributeToken,
|
||||
Tag: compInfo.ModelName,
|
||||
MetaModel: attriModifyConfs[i].attributeExtendName,
|
||||
MetaModel: mod.attributeExtendName,
|
||||
}
|
||||
}
|
||||
|
||||
// batch retrieve table name mappings
|
||||
tableNameMap, err := database.BatchGetProjectNames(tx, identifiers)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
// TODO 增加错误处理日志
|
||||
|
||||
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 {
|
||||
// TODO 增加记录警告
|
||||
updateResults[mod.attributeToken] = errcode.ErrFoundTargetFailed
|
||||
continue
|
||||
}
|
||||
|
||||
updateErr := tx.Table(tableName).
|
||||
Where(fmt.Sprintf("%s = ?", mod.attributeName), mod.attributeOldVal).
|
||||
Updates(map[string]any{mod.attributeName: mod.attributeNewVal}).Error
|
||||
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 updateErr != nil {
|
||||
tx.Rollback()
|
||||
// TODO 增加错误处理日志
|
||||
return
|
||||
if result.Error != nil {
|
||||
updateResults[mod.attributeToken] = errcode.ErrDBUpdateFailed
|
||||
continue
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
updateResults[mod.attributeToken] = errcode.ErrDBzeroAffectedRows
|
||||
continue
|
||||
}
|
||||
|
||||
// init cache item
|
||||
cacheKey := fmt.Sprintf("%s_%s", attributeComponentTag, mod.attributeExtendName)
|
||||
redisUpdateMap[cacheKey] = append(redisUpdateMap[cacheKey], cacheItem{mod.attributeName, mod.attributeNewVal})
|
||||
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 {
|
||||
|
|
@ -136,22 +137,31 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// commit transaction
|
||||
if err := tx.Commit().Error; err != 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resp := network.SuccessResponse{
|
||||
Code: http.StatusOK,
|
||||
Msg: "success",
|
||||
Payload: map[string]interface{}{
|
||||
"uuid": "",
|
||||
},
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
// TODO 通过循环最初的 request 填充剩余处理正确token的updateResults结果
|
||||
renderRespSuccess(c, constants.RespCodeSuccess, "process completed", updateResults)
|
||||
}
|
||||
|
||||
type attributeModifyConfig struct {
|
||||
attributeToken string
|
||||
attributeExtendName string
|
||||
attributeName string
|
||||
attributeOldVal string
|
||||
|
|
@ -159,6 +169,7 @@ type attributeModifyConfig struct {
|
|||
}
|
||||
|
||||
type cacheItem struct {
|
||||
token string
|
||||
name string
|
||||
newVal string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// Package handler provides HTTP handlers for various endpoints.
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"modelRT/network"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func renderFailure(c *gin.Context, code int, msg string, payload any) {
|
||||
resp := network.FailureResponse{
|
||||
Code: code,
|
||||
Msg: msg,
|
||||
}
|
||||
if payload != nil {
|
||||
resp.Payload = payload
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func renderRespSuccess(c *gin.Context, code int, msg string, payload any) {
|
||||
resp := network.SuccessResponse{
|
||||
Code: code,
|
||||
Msg: msg,
|
||||
}
|
||||
if payload != nil {
|
||||
resp.Payload = payload
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ func (p *ProjectManager) TableName() string {
|
|||
|
||||
// ProjectIdentifier define struct to encapsulate query parameter pairs
|
||||
type ProjectIdentifier struct {
|
||||
Token string
|
||||
Tag string
|
||||
MetaModel string
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue