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
|
package errcode
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Package errcode provides internal error definition and business error definition
|
||||||
package errcode
|
package errcode
|
||||||
|
|
||||||
import "errors"
|
import "errors"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// Package errcode provides internal error definition and business error definition
|
||||||
package errcode
|
package errcode
|
||||||
|
|
||||||
import (
|
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 (
|
const (
|
||||||
// SubSuccessCode define subscription success code
|
// SubSuccessCode define subscription success code
|
||||||
SubSuccessCode = "1001"
|
SubSuccessCode = "1001"
|
||||||
|
|
@ -3,9 +3,10 @@ package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"modelRT/common/errcode"
|
||||||
|
"modelRT/constants"
|
||||||
"modelRT/database"
|
"modelRT/database"
|
||||||
"modelRT/diagram"
|
"modelRT/diagram"
|
||||||
"modelRT/logger"
|
"modelRT/logger"
|
||||||
|
|
@ -20,113 +21,113 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
|
||||||
pgClient := database.GetPostgresDBClient()
|
pgClient := database.GetPostgresDBClient()
|
||||||
var request network.ComponentAttributeUpdateInfo
|
var request network.ComponentAttributeUpdateInfo
|
||||||
if err := c.ShouldBindJSON(&request); err != nil {
|
if err := c.ShouldBindJSON(&request); err != nil {
|
||||||
logger.Error(c, "unmarshal circuit diagram component attribute update info failed", "error", err)
|
logger.Error(c, "unmarshal request params failed", "error", err)
|
||||||
|
renderFailure(c, constants.RespCodeInvalidParams, err.Error(), nil)
|
||||||
resp := network.FailureResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
Msg: err.Error(),
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, resp)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
identifiers := make([]orm.ProjectIdentifier, len(request.AttributeConfigs))
|
updateResults := make(map[string]*errcode.AppError)
|
||||||
attriModifyConfs := make([]attributeModifyConfig, len(request.AttributeConfigs))
|
attriModifyConfs := make([]attributeModifyConfig, 0, len(request.AttributeConfigs))
|
||||||
|
|
||||||
var attributeComponentTag string
|
var attributeComponentTag string
|
||||||
for index, attribute := range request.AttributeConfigs {
|
for index, attribute := range request.AttributeConfigs {
|
||||||
slices := strings.Split(attribute.AttributeToken, ".")
|
slices := strings.Split(attribute.AttributeToken, ".")
|
||||||
if len(slices) < 7 {
|
if len(slices) < 7 {
|
||||||
err := fmt.Errorf("index:%d's attribute token format is invalid: %s", index, attribute.AttributeToken)
|
updateResults[attribute.AttributeToken] = errcode.ErrInvalidToken
|
||||||
logger.Error(c, "parse attribute token failed", "error", err)
|
continue
|
||||||
resp := network.FailureResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
|
||||||
Msg: "parse attribute token failed",
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, resp)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentTag := slices[4]
|
componentTag := slices[4]
|
||||||
extendName := slices[5]
|
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
attributeComponentTag = componentTag
|
attributeComponentTag = componentTag
|
||||||
}
|
} else if componentTag != attributeComponentTag {
|
||||||
if componentTag != attributeComponentTag {
|
updateResults[attribute.AttributeToken] = errcode.ErrCrossToken
|
||||||
err := fmt.Errorf("batch update across multiple components is not allowed: [%s] vs [%s]", attributeComponentTag, componentTag)
|
continue
|
||||||
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{
|
attriModifyConfs = append(attriModifyConfs, attributeModifyConfig{
|
||||||
attributeExtendName: extendName,
|
attributeToken: attribute.AttributeToken,
|
||||||
|
attributeExtendName: slices[5],
|
||||||
attributeName: slices[6],
|
attributeName: slices[6],
|
||||||
attributeOldVal: attribute.AttributeOldVal,
|
attributeOldVal: attribute.AttributeOldVal,
|
||||||
attributeNewVal: attribute.AttributeNewVal,
|
attributeNewVal: attribute.AttributeNewVal,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// open transaction
|
// open transaction
|
||||||
tx := pgClient.Begin()
|
tx := pgClient.Begin()
|
||||||
|
|
||||||
// query component base info
|
|
||||||
compInfo, err := database.QueryComponentByCompTag(c, tx, attributeComponentTag)
|
compInfo, err := database.QueryComponentByCompTag(c, tx, attributeComponentTag)
|
||||||
if err != nil {
|
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()
|
tx.Rollback()
|
||||||
resp := network.FailureResponse{
|
|
||||||
Code: http.StatusBadRequest,
|
renderFailure(c, constants.RespCodeFailed, "query component metadata failed", updateResults)
|
||||||
Msg: err.Error(),
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, resp)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range attriModifyConfs {
|
identifiers := make([]orm.ProjectIdentifier, len(attriModifyConfs))
|
||||||
|
for i, mod := range attriModifyConfs {
|
||||||
identifiers[i] = orm.ProjectIdentifier{
|
identifiers[i] = orm.ProjectIdentifier{
|
||||||
|
Token: mod.attributeToken,
|
||||||
Tag: compInfo.ModelName,
|
Tag: compInfo.ModelName,
|
||||||
MetaModel: attriModifyConfs[i].attributeExtendName,
|
MetaModel: mod.attributeExtendName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// batch retrieve table name mappings
|
|
||||||
tableNameMap, err := database.BatchGetProjectNames(tx, identifiers)
|
tableNameMap, err := database.BatchGetProjectNames(tx, identifiers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redisUpdateMap := make(map[string][]cacheItem)
|
redisUpdateMap := make(map[string][]cacheItem)
|
||||||
|
|
||||||
for _, mod := range attriModifyConfs {
|
for _, mod := range attriModifyConfs {
|
||||||
id := orm.ProjectIdentifier{Tag: compInfo.ModelName, MetaModel: mod.attributeExtendName}
|
id := orm.ProjectIdentifier{Tag: compInfo.ModelName, MetaModel: mod.attributeExtendName}
|
||||||
tableName, exists := tableNameMap[id]
|
tableName, exists := tableNameMap[id]
|
||||||
if !exists {
|
if !exists {
|
||||||
// TODO 增加记录警告
|
updateResults[mod.attributeToken] = errcode.ErrFoundTargetFailed
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
updateErr := tx.Table(tableName).
|
result := tx.Table(tableName).
|
||||||
Where(fmt.Sprintf("%s = ?", mod.attributeName), mod.attributeOldVal).
|
Where(fmt.Sprintf("%s = ? AND global_uuid = ?", mod.attributeName), mod.attributeOldVal, compInfo.GlobalUUID).
|
||||||
Updates(map[string]any{mod.attributeName: mod.attributeNewVal}).Error
|
Updates(map[string]any{mod.attributeName: mod.attributeNewVal})
|
||||||
|
|
||||||
if updateErr != nil {
|
if result.Error != nil {
|
||||||
tx.Rollback()
|
updateResults[mod.attributeToken] = errcode.ErrDBUpdateFailed
|
||||||
// TODO 增加错误处理日志
|
continue
|
||||||
return
|
}
|
||||||
|
if result.RowsAffected == 0 {
|
||||||
|
updateResults[mod.attributeToken] = errcode.ErrDBzeroAffectedRows
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// init cache item
|
|
||||||
cacheKey := fmt.Sprintf("%s_%s", attributeComponentTag, mod.attributeExtendName)
|
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 {
|
for key, items := range redisUpdateMap {
|
||||||
|
|
@ -136,22 +137,31 @@ func ComponentAttributeUpdateHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// commit transaction
|
for key, items := range redisUpdateMap {
|
||||||
if err := tx.Commit().Error; err != nil {
|
hset := diagram.NewRedisHash(c, key, 5000, false)
|
||||||
return
|
|
||||||
|
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{
|
// TODO 通过循环最初的 request 填充剩余处理正确token的updateResults结果
|
||||||
Code: http.StatusOK,
|
renderRespSuccess(c, constants.RespCodeSuccess, "process completed", updateResults)
|
||||||
Msg: "success",
|
|
||||||
Payload: map[string]interface{}{
|
|
||||||
"uuid": "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
c.JSON(http.StatusOK, resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type attributeModifyConfig struct {
|
type attributeModifyConfig struct {
|
||||||
|
attributeToken string
|
||||||
attributeExtendName string
|
attributeExtendName string
|
||||||
attributeName string
|
attributeName string
|
||||||
attributeOldVal string
|
attributeOldVal string
|
||||||
|
|
@ -159,6 +169,7 @@ type attributeModifyConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type cacheItem struct {
|
type cacheItem struct {
|
||||||
|
token string
|
||||||
name string
|
name string
|
||||||
newVal 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
|
// ProjectIdentifier define struct to encapsulate query parameter pairs
|
||||||
type ProjectIdentifier struct {
|
type ProjectIdentifier struct {
|
||||||
|
Token string
|
||||||
Tag string
|
Tag string
|
||||||
MetaModel string
|
MetaModel string
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue