This is an automated email from the ASF dual-hosted git repository. linkinstar pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit 7d2fd40097efae5b98049a2ad3b9f15ded2c42fd Author: hgaol <[email protected]> AuthorDate: Wed May 8 00:24:51 2024 +0800 add transaction when add or update reactions and fix other issues --- internal/controller/meta_controller.go | 6 +-- internal/repo/meta/meta_repo.go | 98 ++++++++++++++++++++++++++++++++++ internal/schema/meta_schema.go | 2 +- internal/service/meta/meta_service.go | 80 +-------------------------- 4 files changed, 104 insertions(+), 82 deletions(-) diff --git a/internal/controller/meta_controller.go b/internal/controller/meta_controller.go index fe598b90..2a9ab866 100644 --- a/internal/controller/meta_controller.go +++ b/internal/controller/meta_controller.go @@ -67,9 +67,9 @@ func (mc *MetaController) AddOrUpdateReaction(ctx *gin.Context) { // @Accept json // @Produce json // @Security ApiKeyAuth -// @Param data body schema.UpdateReactionReq true "reaction" -// @Success 200 {object} handler.RespBody -// @Router /answer/api/v1/meta/reaction [put] +// @Param object_id query string true "object_id" +// @Success 200 {object} handler.RespBody{data=schema.ReactionResp} +// @Router /answer/api/v1/meta/reaction [get] func (mc *MetaController) GetReaction(ctx *gin.Context) { req := &schema.GetReactionReq{} if handler.BindAndCheck(ctx, req) { diff --git a/internal/repo/meta/meta_repo.go b/internal/repo/meta/meta_repo.go index bbf3e422..160201cf 100644 --- a/internal/repo/meta/meta_repo.go +++ b/internal/repo/meta/meta_repo.go @@ -21,13 +21,16 @@ package meta import ( "context" + "encoding/json" "github.com/apache/incubator-answer/internal/base/data" "github.com/apache/incubator-answer/internal/base/reason" "github.com/apache/incubator-answer/internal/entity" + "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/internal/service/meta" "github.com/segmentfault/pacman/errors" "xorm.io/builder" + "xorm.io/xorm" ) // metaRepo meta repository @@ -69,6 +72,101 @@ func (mr *metaRepo) UpdateMeta(ctx context.Context, meta *entity.Meta) (err erro return } +// AddOrUpdateMetaByObjectIdAndKey if exist record with same objectID and key, update it. Or create a new one +func (mr *metaRepo) AddOrUpdateMetaByObjectIdAndKey(ctx context.Context, req *schema.UpdateReactionReq) (schema.ReactSummaryMeta, error) { + result, err := mr.data.DB.Transaction(func(session *xorm.Session) (interface{}, error) { + session = session.Context(ctx) + + // 1. acquire meta entity with target object id and key + metaEntity := &entity.Meta{} + exist, err := mr.data.DB.Context(ctx).Where(builder.Eq{"object_id": req.ObjectID}.And(builder.Eq{"`key`": entity.ObjectReactSummaryKey})).ForUpdate().Get(metaEntity) + if err != nil { + return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } + + var reaction schema.ReactSummaryMeta + // if not exist, create new one + if !exist { + reaction = schema.ReactSummaryMeta{} + return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + } else { + err = json.Unmarshal([]byte(metaEntity.Value), &reaction) + if err != nil { + return nil, err + } + } + + // update reaction + mr.updateReaction(req, reaction) + + // write back to meta repo + reactSumBytes, err := json.Marshal(reaction) + if err != nil { + return nil, err + } + + metaObj := &entity.Meta{ + ObjectID: req.ObjectID, + Key: entity.ObjectReactSummaryKey, + Value: string(reactSumBytes), + } + if exist { + _, err = session.Update(metaObj) + } else { + _, err = session.Insert(metaObj) + } + + return reaction, err + }) + + if err != nil { + return nil, errors.InternalServer(reason.DatabaseError).WithError(err) + } + + if ret, ok := result.(schema.ReactSummaryMeta); ok { + return ret, nil + } else { + return nil, errors.InternalServer(reason.UnknownError).WithMsg("Unable to cast to schema.ReactSummaryMeta.") + } +} + +// updateReaction update reaction +func (mr *metaRepo) updateReaction(req *schema.UpdateReactionReq, reaction schema.ReactSummaryMeta) { + emojiUserIds, ok := reaction[req.Emoji] + + if !ok { + emojiUserIds = make([]string, 0) + } + + found := false + for _, item := range emojiUserIds { + if item == req.UserID { + found = true + break + } + } + + removeItem := func(arr []string, target string) []string { + result := make([]string, 0, len(arr)) + + for _, item := range arr { + if item != target { + result = append(result, item) + } + } + + return result + } + + if req.Reaction == "activate" && !found { + emojiUserIds = append(emojiUserIds, req.UserID) + } else if req.Reaction == "deactivate" && found { + emojiUserIds = removeItem(emojiUserIds, req.UserID) + } + + reaction[req.Emoji] = emojiUserIds +} + // GetMetaByObjectIdAndKey get meta one func (mr *metaRepo) GetMetaByObjectIdAndKey(ctx context.Context, objectID, key string) ( meta *entity.Meta, exist bool, err error) { diff --git a/internal/schema/meta_schema.go b/internal/schema/meta_schema.go index cf213813..bf458bfc 100644 --- a/internal/schema/meta_schema.go +++ b/internal/schema/meta_schema.go @@ -22,7 +22,7 @@ package schema type UpdateReactionReq struct { ObjectID string `validate:"required" form:"object_id" json:"object_id"` // object id Emoji string `validate:"required" form:"emoji" json:"emoji"` // emoji - Type string `validate:"required" form:"type" json:"type"` // type + Reaction string `validate:"required" form:"reaction" json:"reaction"` // reaction UserID string `json:"-"` } diff --git a/internal/service/meta/meta_service.go b/internal/service/meta/meta_service.go index cd7ca4f7..b06fa736 100644 --- a/internal/service/meta/meta_service.go +++ b/internal/service/meta/meta_service.go @@ -36,6 +36,7 @@ type MetaRepo interface { AddMeta(ctx context.Context, meta *entity.Meta) (err error) RemoveMeta(ctx context.Context, id int) (err error) UpdateMeta(ctx context.Context, meta *entity.Meta) (err error) + AddOrUpdateMetaByObjectIdAndKey(ctx context.Context, req *schema.UpdateReactionReq) (schema.ReactSummaryMeta, error) GetMetaByObjectIdAndKey(ctx context.Context, objectId, key string) (meta *entity.Meta, exist bool, err error) GetMetaList(ctx context.Context, meta *entity.Meta) (metas []*entity.Meta, err error) } @@ -123,41 +124,7 @@ func (ms *MetaService) GetReactionByObjectId(ctx context.Context, objectID strin // AddOrUpdateReaction add or update reaction func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.UpdateReactionReq) (resp schema.ReactionResp, err error) { - // get reaction for this object - reactionMeta, err := ms.GetMetaByObjectIdAndKey(ctx, req.ObjectID, entity.ObjectReactSummaryKey) - - var reaction schema.ReactSummaryMeta - if err != nil { - var pacmanErr *myErrors.Error - if errors.As(err, &pacmanErr) && pacmanErr.Reason == reason.MetaObjectNotFound { - // create new reaction summary - reaction = schema.ReactSummaryMeta{} - } else { - return nil, err - } - } else { - // json unmarshal reactionMeta.Value to reaction - err = json.Unmarshal([]byte(reactionMeta.Value), &reaction) - if err != nil { - return nil, err - } - } - - // update reaction - ms.updateReaction(req, reaction) - - // write back to meta repo - reactSumBytes, err := json.Marshal(reaction) - if err != nil { - return nil, err - } - - if reactionMeta == nil { - err = ms.AddMeta(ctx, req.ObjectID, entity.ObjectReactSummaryKey, string(reactSumBytes)) - } else { - err = ms.UpdateMeta(ctx, reactionMeta.ID, entity.ObjectReactSummaryKey, string(reactSumBytes)) - } - + reaction, err := ms.metaRepo.AddOrUpdateMetaByObjectIdAndKey(ctx, req) if err != nil { return nil, err } @@ -170,49 +137,6 @@ func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.Upda return resp, nil } -// updateReaction update reaction -func (ms *MetaService) updateReaction(req *schema.UpdateReactionReq, reaction schema.ReactSummaryMeta) { - emojiUserIds, ok := reaction[req.Emoji] - - if !ok { - emojiUserIds = make([]string, 0) - } - - found := false - for _, item := range emojiUserIds { - if item == req.UserID { - found = true - break - } - } - - removeItem := func(arr []string, target string) []string { - result := make([]string, 0, len(arr)) - - for _, item := range arr { - if item != target { - result = append(result, item) - } - } - - return result - } - - if req.Type == "activate" && !found { - emojiUserIds = append(emojiUserIds, req.UserID) - } else if req.Type == "deactivate" && found { - emojiUserIds = removeItem(emojiUserIds, req.UserID) - } else if req.Type == "toggle" { - if found { - emojiUserIds = removeItem(emojiUserIds, req.UserID) - } else { - emojiUserIds = append(emojiUserIds, req.UserID) - } - } - - reaction[req.Emoji] = emojiUserIds -} - func (ms *MetaService) convertToReactionResp(ctx context.Context, reaction schema.ReactSummaryMeta) (schema.ReactionResp, error) { resp := schema.ReactionResp{} // traverse map and convert to username
