Files
wehub-resource-sync 39dbe3a57d
CI / Check and lint (push) Failing after 1s
Lint / Lint (ubuntu-latest) (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:22:32 +08:00

209 lines
6.6 KiB
Go

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package meta
import (
"context"
"encoding/json"
"errors"
"strconv"
"strings"
"github.com/apache/answer/internal/service/eventqueue"
"github.com/apache/answer/internal/base/constant"
"github.com/apache/answer/internal/base/handler"
"github.com/apache/answer/internal/base/reason"
"github.com/apache/answer/internal/base/translator"
"github.com/apache/answer/internal/entity"
"github.com/apache/answer/internal/schema"
answercommon "github.com/apache/answer/internal/service/answer_common"
metacommon "github.com/apache/answer/internal/service/meta_common"
questioncommon "github.com/apache/answer/internal/service/question_common"
usercommon "github.com/apache/answer/internal/service/user_common"
"github.com/apache/answer/pkg/obj"
myErrors "github.com/segmentfault/pacman/errors"
)
// MetaService user service
type MetaService struct {
metaCommonService *metacommon.MetaCommonService
userCommon *usercommon.UserCommon
questionRepo questioncommon.QuestionRepo
answerRepo answercommon.AnswerRepo
eventQueueService eventqueue.Service
}
func NewMetaService(
metaCommonService *metacommon.MetaCommonService,
userCommon *usercommon.UserCommon,
answerRepo answercommon.AnswerRepo,
questionRepo questioncommon.QuestionRepo,
eventQueueService eventqueue.Service,
) *MetaService {
return &MetaService{
metaCommonService: metaCommonService,
questionRepo: questionRepo,
userCommon: userCommon,
answerRepo: answerRepo,
eventQueueService: eventQueueService,
}
}
// GetReactionByObjectId get reaction
func (ms *MetaService) GetReactionByObjectId(ctx context.Context, req *schema.GetReactionReq) (resp *schema.GetReactionByObjectIdResp, err error) {
reactionMeta, err := ms.metaCommonService.GetMetaByObjectIdAndKey(ctx, req.ObjectID, entity.ObjectReactSummaryKey)
// if not exist, return nil
if err != nil {
var pacmanErr *myErrors.Error
if errors.As(err, &pacmanErr) && pacmanErr.Reason == reason.MetaObjectNotFound {
return nil, nil
} else {
return nil, err
}
}
var reaction schema.ReactionsSummaryMeta
err = json.Unmarshal([]byte(reactionMeta.Value), &reaction)
if err != nil {
return nil, err
}
return ms.convertToReactionResp(ctx, req.UserID, &reaction)
}
// AddOrUpdateReaction add or update reaction
func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.UpdateReactionReq) (resp *schema.GetReactionByObjectIdResp, err error) {
// check if object exist and it's answer or question
objectType, err := obj.GetObjectTypeStrByObjectID(req.ObjectID)
if err != nil {
return nil, err
}
var event *schema.EventMsg
switch objectType {
case constant.AnswerObjectType:
answerInfo, exist, err := ms.answerRepo.GetAnswer(ctx, req.ObjectID)
if err != nil {
return nil, err
}
if !exist {
return nil, myErrors.BadRequest(reason.AnswerNotFound)
}
event = schema.NewEvent(constant.EventAnswerReact, req.UserID).TID(answerInfo.ID).
AID(answerInfo.ID, answerInfo.UserID)
case constant.QuestionObjectType:
questionInfo, exist, err := ms.questionRepo.GetQuestion(ctx, req.ObjectID)
if err != nil {
return nil, err
}
if !exist {
return nil, myErrors.BadRequest(reason.QuestionNotFound)
}
event = schema.NewEvent(constant.EventQuestionReact, req.UserID).TID(questionInfo.ID).
QID(questionInfo.ID, questionInfo.UserID)
default:
return nil, myErrors.BadRequest(reason.ObjectNotFound)
}
// add or update
reactions := &schema.ReactionsSummaryMeta{}
err = ms.metaCommonService.AddOrUpdateMetaByObjectIdAndKey(ctx, req.ObjectID, entity.ObjectReactSummaryKey, func(meta *entity.Meta, exist bool) (*entity.Meta, error) {
// if not exist, create new one
if exist {
_ = json.Unmarshal([]byte(meta.Value), reactions)
}
// update reaction
ms.updateReaction(req, reactions)
// write back to meta repo
reactSumBytes, err := json.Marshal(reactions)
if err != nil {
return nil, err
}
return &entity.Meta{
ObjectID: req.ObjectID,
Key: entity.ObjectReactSummaryKey,
Value: string(reactSumBytes),
}, nil
})
if err != nil {
return nil, myErrors.InternalServer(reason.DatabaseError).WithError(err)
}
resp, err = ms.convertToReactionResp(ctx, req.UserID, reactions)
if err != nil {
return nil, err
}
ms.eventQueueService.Send(ctx, event)
return resp, nil
}
// updateReaction update reaction
func (ms *MetaService) updateReaction(req *schema.UpdateReactionReq, reactions *schema.ReactionsSummaryMeta) {
switch req.Reaction {
case "activate":
reactions.AddReactionSummary(req.Emoji, req.UserID)
case "deactivate":
reactions.RemoveReactionSummary(req.Emoji, req.UserID)
}
}
func (ms *MetaService) convertToReactionResp(ctx context.Context, userId string, r *schema.ReactionsSummaryMeta) (
resp *schema.GetReactionByObjectIdResp, err error) {
lang := handler.GetLangByCtx(ctx)
resp = &schema.GetReactionByObjectIdResp{
ReactionSummary: make([]*schema.ReactionRespItem, 0),
}
// traverse map and convert to username
for _, reaction := range r.Reactions {
item := &schema.ReactionRespItem{
Emoji: reaction.Emoji,
IsActive: r.CheckUserInReactionSummary(reaction.Emoji, userId),
}
usernames := make([]string, 0)
userBasicInfos, err := ms.userCommon.BatchUserBasicInfoByID(ctx, reaction.UserIDs)
item.Count = len(userBasicInfos)
if err != nil {
return resp, err
}
// get username
for _, userBasicInfo := range userBasicInfos {
usernames = append(usernames, userBasicInfo.Username)
if len(usernames) == 5 && len(userBasicInfos) > 5 {
item.Tooltip = translator.TrWithData(lang, constant.ReactionTooltipLabel, map[string]string{
"Count": strconv.Itoa(len(userBasicInfos) - 5),
"Names": strings.Join(usernames, ", "),
})
break
}
}
if len(userBasicInfos) <= 5 {
item.Tooltip = strings.Join(usernames, ", ")
}
resp.ReactionSummary = append(resp.ReactionSummary, item)
}
return resp, nil
}