You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by li...@apache.org on 2024/08/08 08:20:14 UTC

(incubator-answer) 01/01: feat(badge): add event for the badge

This is an automated email from the ASF dual-hosted git repository.

linkinstar pushed a commit to branch feat/1.4.0/badges
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git

commit 07e4f549b13216f76a69a897fb73d2a8fcd55de4
Author: LinkinStars <li...@foxmail.com>
AuthorDate: Thu Aug 8 16:19:47 2024 +0800

    feat(badge): add event for the badge
---
 cmd/wire_gen.go                              | 18 +++---
 internal/base/constant/event.go              | 75 ++++++++++++++++++++++
 internal/controller/template_controller.go   | 16 +++++
 internal/repo/badge/badge_event_handler.go   | 57 +++++++++++++++++
 internal/repo/badge/badge_rule.go            | 95 ++++++++++++++++++++++++++++
 internal/repo/badge/event_rule_mapping.go    | 47 ++++++++++++++
 internal/repo/badge/rule.go                  | 43 +++++++++++++
 internal/schema/event_schema.go              | 70 ++++++++++++++++++++
 internal/service/comment/comment_service.go  | 19 +++++-
 internal/service/content/answer_service.go   | 13 ++++
 internal/service/content/question_service.go | 10 +++
 internal/service/content/user_service.go     |  8 +++
 internal/service/content/vote_service.go     | 31 +++++++++
 internal/service/event_queue/event_queue.go  | 69 ++++++++++++++++++++
 internal/service/meta/meta_service.go        | 22 +++++--
 internal/service/provider.go                 |  2 +
 internal/service/report/report_service.go    | 30 ++++++++-
 17 files changed, 611 insertions(+), 14 deletions(-)

diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go
index 4928a122..c6cdc021 100644
--- a/cmd/wire_gen.go
+++ b/cmd/wire_gen.go
@@ -78,6 +78,7 @@ import (
 	config2 "github.com/apache/incubator-answer/internal/service/config"
 	"github.com/apache/incubator-answer/internal/service/content"
 	"github.com/apache/incubator-answer/internal/service/dashboard"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	export2 "github.com/apache/incubator-answer/internal/service/export"
 	"github.com/apache/incubator-answer/internal/service/follow"
 	meta2 "github.com/apache/incubator-answer/internal/service/meta"
@@ -172,7 +173,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
 	metaRepo := meta.NewMetaRepo(dataData)
 	metaCommonService := metacommon.NewMetaCommonService(metaRepo)
 	questionCommon := questioncommon.NewQuestionCommon(questionRepo, answerRepo, voteRepo, followRepo, tagCommonService, userCommon, collectionCommon, answerCommon, metaCommonService, configService, activityQueueService, revisionRepo, dataData)
-	userService := content.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService, userNotificationConfigRepo, userNotificationConfigService, questionCommon)
+	eventQueueService := event_queue.NewEventQueueService()
+	userService := content.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService, userNotificationConfigRepo, userNotificationConfigService, questionCommon, eventQueueService)
 	captchaRepo := captcha.NewCaptchaRepo(dataData)
 	captchaService := action.NewCaptchaService(captchaRepo)
 	userController := controller.NewUserController(authService, userService, captchaService, emailService, siteInfoCommonService, userNotificationConfigService)
@@ -181,7 +183,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
 	objService := object_info.NewObjService(answerRepo, questionRepo, commentCommonRepo, tagCommonRepo, tagCommonService)
 	notificationQueueService := notice_queue.NewNotificationQueueService()
 	externalNotificationQueueService := notice_queue.NewNewQuestionNotificationQueueService()
-	commentService := comment2.NewCommentService(commentRepo, commentCommonRepo, userCommon, objService, voteRepo, emailService, userRepo, notificationQueueService, externalNotificationQueueService, activityQueueService)
+	commentService := comment2.NewCommentService(commentRepo, commentCommonRepo, userCommon, objService, voteRepo, emailService, userRepo, notificationQueueService, externalNotificationQueueService, activityQueueService, eventQueueService)
 	rolePowerRelRepo := role.NewRolePowerRelRepo(dataData)
 	rolePowerRelService := role2.NewRolePowerRelService(rolePowerRelRepo, userRoleRelService)
 	rankService := rank2.NewRankService(userCommon, userRankRepo, objService, userRoleRelService, rolePowerRelService, configService)
@@ -194,13 +196,13 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
 	externalNotificationService := notification.NewExternalNotificationService(dataData, userNotificationConfigRepo, followRepo, emailService, userRepo, externalNotificationQueueService, userExternalLoginRepo, siteInfoCommonService)
 	reviewRepo := review.NewReviewRepo(dataData)
 	reviewService := review2.NewReviewService(reviewRepo, objService, userCommon, userRepo, questionRepo, answerRepo, userRoleRelService, externalNotificationQueueService, tagCommonService, questionCommon, notificationQueueService, siteInfoCommonService)
-	questionService := content.NewQuestionService(questionRepo, answerRepo, tagCommonService, questionCommon, userCommon, userRepo, userRoleRelService, revisionService, metaCommonService, collectionCommon, answerActivityService, emailService, notificationQueueService, externalNotificationQueueService, activityQueueService, siteInfoCommonService, externalNotificationService, reviewService, configService)
-	answerService := content.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService, notificationQueueService, externalNotificationQueueService, activityQueueService, reviewService)
+	questionService := content.NewQuestionService(questionRepo, answerRepo, tagCommonService, questionCommon, userCommon, userRepo, userRoleRelService, revisionService, metaCommonService, collectionCommon, answerActivityService, emailService, notificationQueueService, externalNotificationQueueService, activityQueueService, siteInfoCommonService, externalNotificationService, reviewService, configService, eventQueueService)
+	answerService := content.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService, notificationQueueService, externalNotificationQueueService, activityQueueService, reviewService, eventQueueService)
 	reportHandle := report_handle.NewReportHandle(questionService, answerService, commentService)
-	reportService := report2.NewReportService(reportRepo, objService, userCommon, answerRepo, questionRepo, commentCommonRepo, reportHandle, configService)
+	reportService := report2.NewReportService(reportRepo, objService, userCommon, answerRepo, questionRepo, commentCommonRepo, reportHandle, configService, eventQueueService)
 	reportController := controller.NewReportController(reportService, rankService, captchaService)
 	contentVoteRepo := activity.NewVoteRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
-	voteService := content.NewVoteService(contentVoteRepo, configService, questionRepo, answerRepo, commentCommonRepo, objService)
+	voteService := content.NewVoteService(contentVoteRepo, configService, questionRepo, answerRepo, commentCommonRepo, objService, eventQueueService)
 	voteController := controller.NewVoteController(voteService, rankService, captchaService)
 	tagService := tag2.NewTagService(tagRepo, tagCommonService, revisionService, followRepo, siteInfoCommonService, activityQueueService)
 	tagController := controller.NewTagController(tagService, tagCommonService, rankService)
@@ -251,7 +253,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
 	permissionController := controller.NewPermissionController(rankService)
 	userPluginController := controller.NewUserPluginController(pluginCommonService)
 	reviewController := controller.NewReviewController(reviewService, rankService, captchaService)
-	metaService := meta2.NewMetaService(metaCommonService, userCommon, answerRepo, questionRepo)
+	metaService := meta2.NewMetaService(metaCommonService, userCommon, answerRepo, questionRepo, eventQueueService)
 	metaController := controller.NewMetaController(metaService)
 	answerAPIRouter := router.NewAnswerAPIRouter(langController, userController, commentController, reportController, voteController, tagController, followController, collectionController, questionController, answerController, searchController, revisionController, rankController, userAdminController, reasonController, themeController, siteInfoController, controllerSiteInfoController, notificationController, dashboardController, uploadController, activityController, roleController, pluginCon [...]
 	swaggerRouter := router.NewSwaggerRouter(swaggerConf)
@@ -260,7 +262,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
 	avatarMiddleware := middleware.NewAvatarMiddleware(serviceConf, uploaderService)
 	shortIDMiddleware := middleware.NewShortIDMiddleware(siteInfoCommonService)
 	templateRenderController := templaterender.NewTemplateRenderController(questionService, userService, tagService, answerService, commentService, siteInfoCommonService, questionRepo)
-	templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService)
+	templateController := controller.NewTemplateController(templateRenderController, siteInfoCommonService, eventQueueService, userService)
 	templateRouter := router.NewTemplateRouter(templateController, templateRenderController, siteInfoController, authUserMiddleware)
 	connectorController := controller.NewConnectorController(siteInfoCommonService, emailService, userExternalLoginService)
 	userCenterLoginService := user_external_login2.NewUserCenterLoginService(userRepo, userCommon, userExternalLoginRepo, userActiveActivityRepo, siteInfoCommonService)
diff --git a/internal/base/constant/event.go b/internal/base/constant/event.go
new file mode 100644
index 00000000..f7fd8412
--- /dev/null
+++ b/internal/base/constant/event.go
@@ -0,0 +1,75 @@
+/*
+ * 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 constant
+
+// EventType event type. It is used to define the type of event. Such as object.action
+type EventType string
+
+// event object
+const (
+	eventQuestion = "question"
+	eventAnswer   = "answer"
+	eventComment  = "comment"
+	eventUser     = "user"
+)
+
+// event action
+const (
+	eventCreate = "create"
+	eventUpdate = "update"
+	eventDelete = "delete"
+	eventVote   = "vote"
+	eventAccept = "accept" // only question have the accept event
+	eventShare  = "share"  // the object share link has been clicked
+	eventFlag   = "flag"
+	eventReact  = "react"
+)
+
+const (
+	EventUserUpdate EventType = eventUser + "." + eventUpdate
+	EventUserShare  EventType = eventUser + "." + eventShare
+)
+
+const (
+	EventQuestionCreate EventType = eventQuestion + "." + eventCreate
+	EventQuestionUpdate EventType = eventQuestion + "." + eventUpdate
+	EventQuestionDelete EventType = eventQuestion + "." + eventDelete
+	EventQuestionVote   EventType = eventQuestion + "." + eventVote
+	EventQuestionAccept EventType = eventQuestion + "." + eventAccept
+	EventQuestionFlag   EventType = eventQuestion + "." + eventFlag
+	EventQuestionReact  EventType = eventQuestion + "." + eventReact
+)
+
+const (
+	EventAnswerCreate EventType = eventAnswer + "." + eventCreate
+	EventAnswerUpdate EventType = eventAnswer + "." + eventUpdate
+	EventAnswerDelete EventType = eventAnswer + "." + eventDelete
+	EventAnswerVote   EventType = eventAnswer + "." + eventVote
+	EventAnswerFlag   EventType = eventAnswer + "." + eventFlag
+	EventAnswerReact  EventType = eventAnswer + "." + eventReact
+)
+
+const (
+	EventCommentCreate EventType = eventComment + "." + eventCreate
+	EventCommentUpdate EventType = eventComment + "." + eventUpdate
+	EventCommentDelete EventType = eventComment + "." + eventDelete
+	EventCommentVote   EventType = eventComment + "." + eventVote
+	EventCommentFlag   EventType = eventComment + "." + eventFlag
+)
diff --git a/internal/controller/template_controller.go b/internal/controller/template_controller.go
index 09f5bffc..a786f646 100644
--- a/internal/controller/template_controller.go
+++ b/internal/controller/template_controller.go
@@ -22,6 +22,8 @@ package controller
 import (
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-answer/internal/service/content"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"github.com/apache/incubator-answer/plugin"
 	"html/template"
 	"net/http"
@@ -54,12 +56,16 @@ type TemplateController struct {
 	cssPath                  string
 	templateRenderController *templaterender.TemplateRenderController
 	siteInfoService          siteinfo_common.SiteInfoCommonService
+	eventQueueService        event_queue.EventQueueService
+	userService              *content.UserService
 }
 
 // NewTemplateController new controller
 func NewTemplateController(
 	templateRenderController *templaterender.TemplateRenderController,
 	siteInfoService siteinfo_common.SiteInfoCommonService,
+	eventQueueService event_queue.EventQueueService,
+	userService *content.UserService,
 ) *TemplateController {
 	script, css := GetStyle()
 	return &TemplateController{
@@ -67,6 +73,8 @@ func NewTemplateController(
 		cssPath:                  css,
 		templateRenderController: templateRenderController,
 		siteInfoService:          siteInfoService,
+		eventQueueService:        eventQueueService,
+		userService:              userService,
 	}
 }
 func GetStyle() (script []string, css string) {
@@ -271,6 +279,7 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) {
 	id := ctx.Param("id")
 	title := ctx.Param("title")
 	answerid := ctx.Param("answerid")
+	shareUsername := ctx.Query("share")
 	if checker.IsQuestionsIgnorePath(id) {
 		// if id == "ask" {
 		file, err := ui.Build.ReadFile("build/index.html")
@@ -291,6 +300,13 @@ func (tc *TemplateController) QuestionInfo(ctx *gin.Context) {
 		tc.Page404(ctx)
 		return
 	}
+	if len(shareUsername) > 0 {
+		userInfo, err := tc.userService.GetOtherUserInfoByUsername(
+			ctx, &schema.GetOtherUserInfoByUsernameReq{Username: shareUsername})
+		if err == nil {
+			tc.eventQueueService.Send(ctx, schema.NewEvent(constant.EventUserShare, userInfo.ID))
+		}
+	}
 	encodeTitle := htmltext.UrlTitle(detail.Title)
 	if encodeTitle == title {
 		correctTitle = true
diff --git a/internal/repo/badge/badge_event_handler.go b/internal/repo/badge/badge_event_handler.go
new file mode 100644
index 00000000..242778ec
--- /dev/null
+++ b/internal/repo/badge/badge_event_handler.go
@@ -0,0 +1,57 @@
+/*
+ * 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 badge
+
+import (
+	"context"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
+	"github.com/segmentfault/pacman/log"
+
+	"github.com/apache/incubator-answer/internal/base/data"
+	"github.com/apache/incubator-answer/internal/schema"
+)
+
+type BadgeEventService struct {
+	data              *data.Data
+	eventQueueService event_queue.EventQueueService
+}
+
+func NewBadgeEventService(
+	data *data.Data,
+	eventQueueService event_queue.EventQueueService,
+) *BadgeEventService {
+	n := &BadgeEventService{
+		data:              data,
+		eventQueueService: eventQueueService,
+	}
+	eventQueueService.RegisterHandler(n.Handler)
+	return n
+}
+
+func (ns *BadgeEventService) Handler(ctx context.Context, msg *schema.EventMsg) error {
+	log.Debugf("received badge event %+v", msg)
+	// TODO: Check if badge already exists
+
+	// TODO: Check rule
+
+	// TODO: Distribute badge
+
+	return nil
+}
diff --git a/internal/repo/badge/badge_rule.go b/internal/repo/badge/badge_rule.go
new file mode 100644
index 00000000..3ae0b7b4
--- /dev/null
+++ b/internal/repo/badge/badge_rule.go
@@ -0,0 +1,95 @@
+/*
+ * 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 badge
+
+import (
+	"context"
+	"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/service/unique"
+	"github.com/segmentfault/pacman/errors"
+)
+
+// BadgeRuleRepo collection repository
+type BadgeRuleRepo struct {
+	data         *data.Data
+	uniqueIDRepo unique.UniqueIDRepo
+}
+
+// FilledPersonalProfile filled personal profile
+func (br *BadgeRuleRepo) FilledPersonalProfile(ctx context.Context, userID string) (reach bool, err error) {
+	bean := &entity.User{ID: userID}
+	exist, err := br.data.DB.Context(ctx).Get(bean)
+	if err != nil {
+		return false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
+	}
+	if !exist {
+		return false, nil
+	}
+	if len(bean.Bio) > 0 {
+		return true, nil
+	}
+	return false, nil
+}
+
+// FirstPostEdit first post edit
+func (br *BadgeRuleRepo) FirstPostEdit(ctx context.Context, userID string, objectID string) {
+
+}
+
+// FirstFlaggedPost first flagged post.
+func (br *BadgeRuleRepo) FirstFlaggedPost(ctx context.Context, userID string, reportID string) {
+}
+
+// FirstVotedPost first voted post
+func (br *BadgeRuleRepo) FirstVotedPost(ctx context.Context) {
+
+}
+
+// FirstReactedPost first reacted post
+func (br *BadgeRuleRepo) FirstReactedPost(ctx context.Context) {
+
+}
+
+// FirstSharedPost first shared post
+func (br *BadgeRuleRepo) FirstSharedPost(ctx context.Context) {
+
+}
+
+// AskQuestionAcceptAnswer ask question accept answer
+func (br *BadgeRuleRepo) AskQuestionAcceptAnswer(ctx context.Context) {
+
+}
+
+// AnswerAccepted answer accepted
+func (br *BadgeRuleRepo) AnswerAccepted(ctx context.Context) {
+
+}
+
+// ReachAnswerScore reach answer score
+func (br *BadgeRuleRepo) ReachAnswerScore(ctx context.Context) {
+
+}
+
+// ReachQuestionScore reach question score
+func (br *BadgeRuleRepo) ReachQuestionScore(ctx context.Context) {
+
+}
diff --git a/internal/repo/badge/event_rule_mapping.go b/internal/repo/badge/event_rule_mapping.go
new file mode 100644
index 00000000..8f01fdef
--- /dev/null
+++ b/internal/repo/badge/event_rule_mapping.go
@@ -0,0 +1,47 @@
+/*
+ * 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 badge
+
+import "github.com/apache/incubator-answer/internal/base/constant"
+
+var (
+	EventRuleMapping = map[constant.EventType][]string{
+		constant.EventUserUpdate:     {FilledPersonalProfile},
+		constant.EventUserShare:      {FirstSharedPost},
+		constant.EventQuestionCreate: {""},
+		constant.EventQuestionUpdate: {FirstPostEdit},
+		constant.EventQuestionDelete: {""},
+		constant.EventQuestionVote:   {FirstVotedPost, ReachQuestionScore},
+		constant.EventQuestionAccept: {AskQuestionAcceptAnswer, AnswerAccepted},
+		constant.EventQuestionFlag:   {FirstFlaggedPost},
+		constant.EventQuestionReact:  {FirstReactedPost},
+		constant.EventAnswerCreate:   {""},
+		constant.EventAnswerUpdate:   {FirstPostEdit},
+		constant.EventAnswerDelete:   {""},
+		constant.EventAnswerVote:     {FirstVotedPost, ReachAnswerScore},
+		constant.EventAnswerFlag:     {FirstFlaggedPost},
+		constant.EventAnswerReact:    {FirstReactedPost},
+		constant.EventCommentCreate:  {""},
+		constant.EventCommentUpdate:  {""},
+		constant.EventCommentDelete:  {""},
+		constant.EventCommentVote:    {FirstVotedPost},
+		constant.EventCommentFlag:    {FirstFlaggedPost},
+	}
+)
diff --git a/internal/repo/badge/rule.go b/internal/repo/badge/rule.go
new file mode 100644
index 00000000..635b30da
--- /dev/null
+++ b/internal/repo/badge/rule.go
@@ -0,0 +1,43 @@
+/*
+ * 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 badge
+
+const (
+	// FilledPersonalProfile filled personal profile
+	FilledPersonalProfile = "filled_personal_profile"
+	// FirstPostEdit first post edit
+	FirstPostEdit = "first_post_edit"
+	// FirstFlaggedPost first flagged post.
+	FirstFlaggedPost = "first_flagged_post"
+	// FirstVotedPost first voted post
+	FirstVotedPost = "first_voted_post"
+	// FirstReactedPost first reacted post
+	FirstReactedPost = "first_reacted_post"
+	// FirstSharedPost first shared post
+	FirstSharedPost = "first_shared_post"
+	// AskQuestionAcceptAnswer ask question accept answer
+	AskQuestionAcceptAnswer = "ask_question_accept_answer"
+	// AnswerAccepted answer accepted
+	AnswerAccepted = "answer_accepted"
+	// ReachAnswerScore reach answer score
+	ReachAnswerScore = "reach_answer_score"
+	// ReachQuestionScore reach question score
+	ReachQuestionScore = "reach_question_score"
+)
diff --git a/internal/schema/event_schema.go b/internal/schema/event_schema.go
new file mode 100644
index 00000000..01f80fbb
--- /dev/null
+++ b/internal/schema/event_schema.go
@@ -0,0 +1,70 @@
+/*
+ * 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 schema
+
+import "github.com/apache/incubator-answer/internal/base/constant"
+
+// EventMsg event message
+type EventMsg struct {
+	EventType constant.EventType
+	UserID    string
+
+	QuestionID     string
+	QuestionUserID string
+
+	AnswerID     string
+	AnswerUserID string
+
+	CommentID     string
+	CommentUserID string
+
+	ExtraInfo map[string]string
+}
+
+func NewEvent(e constant.EventType, userID string) *EventMsg {
+	return &EventMsg{
+		UserID:    userID,
+		EventType: e,
+		ExtraInfo: make(map[string]string),
+	}
+}
+
+func (e *EventMsg) QID(questionID, userID string) *EventMsg {
+	e.QuestionID = questionID
+	e.QuestionUserID = userID
+	return e
+}
+
+func (e *EventMsg) AID(answerID, userID string) *EventMsg {
+	e.AnswerID = answerID
+	e.AnswerUserID = userID
+	return e
+}
+
+func (e *EventMsg) CID(comment, userID string) *EventMsg {
+	e.CommentID = comment
+	e.CommentUserID = userID
+	return e
+}
+
+func (e *EventMsg) AddExtra(key, value string) *EventMsg {
+	e.ExtraInfo[key] = value
+	return e
+}
diff --git a/internal/service/comment/comment_service.go b/internal/service/comment/comment_service.go
index 9f2c45f1..75d30f9d 100644
--- a/internal/service/comment/comment_service.go
+++ b/internal/service/comment/comment_service.go
@@ -21,6 +21,7 @@ package comment
 
 import (
 	"context"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"time"
 
 	"github.com/apache/incubator-answer/internal/base/constant"
@@ -86,6 +87,7 @@ type CommentService struct {
 	notificationQueueService         notice_queue.NotificationQueueService
 	externalNotificationQueueService notice_queue.ExternalNotificationQueueService
 	activityQueueService             activity_queue.ActivityQueueService
+	eventQueueService                event_queue.EventQueueService
 }
 
 // NewCommentService new comment service
@@ -100,6 +102,7 @@ func NewCommentService(
 	notificationQueueService notice_queue.NotificationQueueService,
 	externalNotificationQueueService notice_queue.ExternalNotificationQueueService,
 	activityQueueService activity_queue.ActivityQueueService,
+	eventQueueService event_queue.EventQueueService,
 ) *CommentService {
 	return &CommentService{
 		commentRepo:                      commentRepo,
@@ -112,6 +115,7 @@ func NewCommentService(
 		notificationQueueService:         notificationQueueService,
 		externalNotificationQueueService: externalNotificationQueueService,
 		activityQueueService:             activityQueueService,
+		eventQueueService:                eventQueueService,
 	}
 }
 
@@ -184,13 +188,19 @@ func (cs *CommentService) AddComment(ctx context.Context, req *schema.AddComment
 		OriginalObjectID: req.ObjectID,
 		ActivityTypeKey:  constant.ActQuestionCommented,
 	}
+	var event *schema.EventMsg
 	switch objInfo.ObjectType {
 	case constant.QuestionObjectType:
 		activityMsg.ActivityTypeKey = constant.ActQuestionCommented
+		event = schema.NewEvent(constant.EventCommentCreate, req.UserID).
+			CID(comment.ID, comment.UserID).QID(objInfo.QuestionID, objInfo.ObjectCreatorUserID)
 	case constant.AnswerObjectType:
 		activityMsg.ActivityTypeKey = constant.ActAnswerCommented
+		event = schema.NewEvent(constant.EventCommentCreate, req.UserID).
+			CID(comment.ID, comment.UserID).AID(objInfo.AnswerID, objInfo.ObjectCreatorUserID)
 	}
 	cs.activityQueueService.Send(ctx, activityMsg)
+	cs.eventQueueService.Send(ctx, event)
 	return resp, nil
 }
 
@@ -241,7 +251,12 @@ func (cs *CommentService) addCommentNotification(
 
 // RemoveComment delete comment
 func (cs *CommentService) RemoveComment(ctx context.Context, req *schema.RemoveCommentReq) (err error) {
-	return cs.commentRepo.RemoveComment(ctx, req.CommentID)
+	err = cs.commentRepo.RemoveComment(ctx, req.CommentID)
+	if err != nil {
+		return err
+	}
+	cs.eventQueueService.Send(ctx, schema.NewEvent(constant.EventCommentDelete, req.UserID).CID(req.CommentID, req.UserID))
+	return nil
 }
 
 // UpdateComment update comment
@@ -273,6 +288,8 @@ func (cs *CommentService) UpdateComment(ctx context.Context, req *schema.UpdateC
 		OriginalText: req.OriginalText,
 		ParsedText:   req.ParsedText,
 	}
+	cs.eventQueueService.Send(ctx, schema.NewEvent(constant.EventCommentUpdate, req.UserID).
+		CID(old.ID, old.UserID))
 	return resp, nil
 }
 
diff --git a/internal/service/content/answer_service.go b/internal/service/content/answer_service.go
index f8feda8a..5674a79b 100644
--- a/internal/service/content/answer_service.go
+++ b/internal/service/content/answer_service.go
@@ -22,6 +22,7 @@ package content
 import (
 	"context"
 	"encoding/json"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"time"
 
 	"github.com/apache/incubator-answer/internal/base/constant"
@@ -67,6 +68,7 @@ type AnswerService struct {
 	externalNotificationQueueService notice_queue.ExternalNotificationQueueService
 	activityQueueService             activity_queue.ActivityQueueService
 	reviewService                    *review.ReviewService
+	eventQueueService                event_queue.EventQueueService
 }
 
 func NewAnswerService(
@@ -86,6 +88,7 @@ func NewAnswerService(
 	externalNotificationQueueService notice_queue.ExternalNotificationQueueService,
 	activityQueueService activity_queue.ActivityQueueService,
 	reviewService *review.ReviewService,
+	eventQueueService event_queue.EventQueueService,
 ) *AnswerService {
 	return &AnswerService{
 		answerRepo:                       answerRepo,
@@ -104,6 +107,7 @@ func NewAnswerService(
 		externalNotificationQueueService: externalNotificationQueueService,
 		activityQueueService:             activityQueueService,
 		reviewService:                    reviewService,
+		eventQueueService:                eventQueueService,
 	}
 }
 
@@ -175,6 +179,8 @@ func (as *AnswerService) RemoveAnswer(ctx context.Context, req *schema.RemoveAns
 		OriginalObjectID: answerInfo.ID,
 		ActivityTypeKey:  constant.ActAnswerDeleted,
 	})
+	as.eventQueueService.Send(ctx, schema.NewEvent(constant.EventAnswerDelete, req.UserID).
+		AID(answerInfo.ID, answerInfo.UserID))
 	return
 }
 
@@ -295,6 +301,8 @@ func (as *AnswerService) Insert(ctx context.Context, req *schema.AnswerAddReq) (
 		OriginalObjectID: questionInfo.ID,
 		ActivityTypeKey:  constant.ActQuestionAnswered,
 	})
+	as.eventQueueService.Send(ctx, schema.NewEvent(constant.EventAnswerCreate, req.UserID).
+		AID(insertData.ID, insertData.UserID))
 	return insertData.ID, nil
 }
 
@@ -383,6 +391,8 @@ func (as *AnswerService) Update(ctx context.Context, req *schema.AnswerUpdateReq
 			ActivityTypeKey:  constant.ActAnswerEdited,
 			RevisionID:       revisionID,
 		})
+		as.eventQueueService.Send(ctx, schema.NewEvent(constant.EventAnswerUpdate, req.UserID).
+			AID(insertData.ID, insertData.UserID))
 	}
 
 	return insertData.ID, nil
@@ -436,6 +446,9 @@ func (as *AnswerService) AcceptAnswer(ctx context.Context, req *schema.AcceptAns
 		oldAnswerInfo.ID = uid.DeShortID(oldAnswerInfo.ID)
 	}
 
+	as.eventQueueService.Send(ctx, schema.NewEvent(constant.EventQuestionAccept, req.UserID).
+		QID(questionInfo.ID, questionInfo.UserID).AID(req.AnswerID, req.UserID))
+
 	as.updateAnswerRank(ctx, req.UserID, questionInfo, acceptedAnswerInfo, oldAnswerInfo)
 	return nil
 }
diff --git a/internal/service/content/question_service.go b/internal/service/content/question_service.go
index 6d8b37ba..dfb778ea 100644
--- a/internal/service/content/question_service.go
+++ b/internal/service/content/question_service.go
@@ -23,6 +23,7 @@ import (
 	"encoding/json"
 	"fmt"
 	answercommon "github.com/apache/incubator-answer/internal/service/answer_common"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"strings"
 	"time"
 
@@ -84,6 +85,7 @@ type QuestionService struct {
 	newQuestionNotificationService   *notification.ExternalNotificationService
 	reviewService                    *review.ReviewService
 	configService                    *config.ConfigService
+	eventQueueService                event_queue.EventQueueService
 }
 
 func NewQuestionService(
@@ -106,6 +108,7 @@ func NewQuestionService(
 	newQuestionNotificationService *notification.ExternalNotificationService,
 	reviewService *review.ReviewService,
 	configService *config.ConfigService,
+	eventQueueService event_queue.EventQueueService,
 ) *QuestionService {
 	return &QuestionService{
 		questionRepo:                     questionRepo,
@@ -127,6 +130,7 @@ func NewQuestionService(
 		newQuestionNotificationService:   newQuestionNotificationService,
 		reviewService:                    reviewService,
 		configService:                    configService,
+		eventQueueService:                eventQueueService,
 	}
 }
 
@@ -385,6 +389,8 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
 		qs.externalNotificationQueueService.Send(ctx,
 			schema.CreateNewQuestionNotificationMsg(question.ID, question.Title, question.UserID, tags))
 	}
+	qs.eventQueueService.Send(ctx, schema.NewEvent(constant.EventQuestionCreate, req.UserID).
+		QID(question.ID, question.UserID))
 
 	questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
 	return
@@ -546,6 +552,8 @@ func (qs *QuestionService) RemoveQuestion(ctx context.Context, req *schema.Remov
 		OriginalObjectID: questionInfo.ID,
 		ActivityTypeKey:  constant.ActQuestionDeleted,
 	})
+	qs.eventQueueService.Send(ctx, schema.NewEvent(constant.EventQuestionDelete, req.UserID).
+		QID(questionInfo.ID, questionInfo.UserID))
 	return nil
 }
 
@@ -937,6 +945,8 @@ func (qs *QuestionService) UpdateQuestion(ctx context.Context, req *schema.Quest
 			RevisionID:       revisionID,
 			OriginalObjectID: question.ID,
 		})
+		qs.eventQueueService.Send(ctx, schema.NewEvent(constant.EventQuestionUpdate, req.UserID).
+			QID(question.ID, question.UserID))
 	}
 
 	questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
diff --git a/internal/service/content/user_service.go b/internal/service/content/user_service.go
index 11f3bb63..2a6122ae 100644
--- a/internal/service/content/user_service.go
+++ b/internal/service/content/user_service.go
@@ -23,6 +23,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"time"
 
 	"github.com/apache/incubator-answer/internal/base/constant"
@@ -65,6 +66,7 @@ type UserService struct {
 	userNotificationConfigRepo    user_notification_config.UserNotificationConfigRepo
 	userNotificationConfigService *user_notification_config.UserNotificationConfigService
 	questionService               *questioncommon.QuestionCommon
+	eventQueueService             event_queue.EventQueueService
 }
 
 func NewUserService(userRepo usercommon.UserRepo,
@@ -79,6 +81,7 @@ func NewUserService(userRepo usercommon.UserRepo,
 	userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo,
 	userNotificationConfigService *user_notification_config.UserNotificationConfigService,
 	questionService *questioncommon.QuestionCommon,
+	eventQueueService event_queue.EventQueueService,
 ) *UserService {
 	return &UserService{
 		userCommonService:             userCommonService,
@@ -93,6 +96,7 @@ func NewUserService(userRepo usercommon.UserRepo,
 		userNotificationConfigRepo:    userNotificationConfigRepo,
 		userNotificationConfigService: userNotificationConfigService,
 		questionService:               questionService,
+		eventQueueService:             eventQueueService,
 	}
 }
 
@@ -352,6 +356,10 @@ func (us *UserService) UpdateInfo(ctx context.Context, req *schema.UpdateInfoReq
 
 	cond := us.formatUserInfoForUpdateInfo(oldUserInfo, req, siteUsers)
 	err = us.userRepo.UpdateInfo(ctx, cond)
+	if err != nil {
+		return nil, err
+	}
+	us.eventQueueService.Send(ctx, schema.NewEvent(constant.EventUserUpdate, req.UserID))
 	return nil, err
 }
 
diff --git a/internal/service/content/vote_service.go b/internal/service/content/vote_service.go
index bfb403c9..c83011a9 100644
--- a/internal/service/content/vote_service.go
+++ b/internal/service/content/vote_service.go
@@ -21,6 +21,8 @@ package content
 
 import (
 	"context"
+	"fmt"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"strings"
 
 	"github.com/apache/incubator-answer/internal/service/activity_common"
@@ -62,6 +64,7 @@ type VoteService struct {
 	commentCommonRepo comment_common.CommentCommonRepo
 	objectService     *object_info.ObjService
 	activityRepo      activity_common.ActivityRepo
+	eventQueueService event_queue.EventQueueService
 }
 
 func NewVoteService(
@@ -71,6 +74,7 @@ func NewVoteService(
 	answerRepo answercommon.AnswerRepo,
 	commentCommonRepo comment_common.CommentCommonRepo,
 	objectService *object_info.ObjService,
+	eventQueueService event_queue.EventQueueService,
 ) *VoteService {
 	return &VoteService{
 		voteRepo:          voteRepo,
@@ -79,6 +83,7 @@ func NewVoteService(
 		answerRepo:        answerRepo,
 		commentCommonRepo: commentCommonRepo,
 		objectService:     objectService,
+		eventQueueService: eventQueueService,
 	}
 }
 
@@ -112,6 +117,9 @@ func (vs *VoteService) VoteUp(ctx context.Context, req *schema.VoteReq) (resp *s
 			return nil, err
 		}
 		err = vs.voteRepo.Vote(ctx, voteUpOperationInfo)
+		if err != nil {
+			return nil, err
+		}
 	}
 	if err != nil {
 		return nil, err
@@ -125,6 +133,7 @@ func (vs *VoteService) VoteUp(ctx context.Context, req *schema.VoteReq) (resp *s
 	resp.Votes = resp.UpVotes - resp.DownVotes
 	if !req.IsCancel {
 		resp.VoteStatus = constant.ActVoteUp
+		vs.sendEvent(ctx, req, objectInfo, resp)
 	}
 	return resp, nil
 }
@@ -173,6 +182,7 @@ func (vs *VoteService) VoteDown(ctx context.Context, req *schema.VoteReq) (resp
 	resp.Votes = resp.UpVotes - resp.DownVotes
 	if !req.IsCancel {
 		resp.VoteStatus = constant.ActVoteDown
+		vs.sendEvent(ctx, req, objectInfo, resp)
 	}
 	return resp, nil
 }
@@ -289,3 +299,24 @@ func (vs *VoteService) getActivities(ctx context.Context, op *schema.VoteOperati
 	}
 	return activities
 }
+
+func (vs *VoteService) sendEvent(ctx context.Context,
+	req *schema.VoteReq, objectInfo *schema.SimpleObjectInfo, resp *schema.VoteResp) {
+	var event *schema.EventMsg
+	switch objectInfo.ObjectType {
+	case constant.QuestionObjectType:
+		event = schema.NewEvent(constant.EventQuestionVote, req.UserID).
+			QID(objectInfo.QuestionID, objectInfo.ObjectCreatorUserID)
+	case constant.AnswerObjectType:
+		event = schema.NewEvent(constant.EventAnswerVote, req.UserID).
+			AID(objectInfo.AnswerID, objectInfo.ObjectCreatorUserID)
+	case constant.CommentObjectType:
+		event = schema.NewEvent(constant.EventCommentVote, req.UserID).
+			CID(objectInfo.CommentID, objectInfo.ObjectCreatorUserID)
+	default:
+		return
+	}
+	event.AddExtra("vote_up_amount", fmt.Sprintf("%d", resp.UpVotes))
+	event.AddExtra("vote_down_amount", fmt.Sprintf("%d", resp.DownVotes))
+	vs.eventQueueService.Send(ctx, event)
+}
diff --git a/internal/service/event_queue/event_queue.go b/internal/service/event_queue/event_queue.go
new file mode 100644
index 00000000..b89a3ccc
--- /dev/null
+++ b/internal/service/event_queue/event_queue.go
@@ -0,0 +1,69 @@
+/*
+ * 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 event_queue
+
+import (
+	"context"
+
+	"github.com/apache/incubator-answer/internal/schema"
+	"github.com/segmentfault/pacman/log"
+)
+
+type EventQueueService interface {
+	Send(ctx context.Context, msg *schema.EventMsg)
+	RegisterHandler(handler func(ctx context.Context, msg *schema.EventMsg) error)
+}
+
+type eventQueueService struct {
+	Queue   chan *schema.EventMsg
+	Handler func(ctx context.Context, msg *schema.EventMsg) error
+}
+
+func (ns *eventQueueService) Send(ctx context.Context, msg *schema.EventMsg) {
+	ns.Queue <- msg
+}
+
+func (ns *eventQueueService) RegisterHandler(
+	handler func(ctx context.Context, msg *schema.EventMsg) error) {
+	ns.Handler = handler
+}
+
+func (ns *eventQueueService) working() {
+	go func() {
+		for msg := range ns.Queue {
+			log.Debugf("received badge %+v", msg)
+			if ns.Handler == nil {
+				log.Warnf("no handler for badge")
+				continue
+			}
+			if err := ns.Handler(context.Background(), msg); err != nil {
+				log.Error(err)
+			}
+		}
+	}()
+}
+
+// NewEventQueueService create a new badge queue service
+func NewEventQueueService() EventQueueService {
+	ns := &eventQueueService{}
+	ns.Queue = make(chan *schema.EventMsg, 128)
+	ns.working()
+	return ns
+}
diff --git a/internal/service/meta/meta_service.go b/internal/service/meta/meta_service.go
index 1026b173..778c6ca6 100644
--- a/internal/service/meta/meta_service.go
+++ b/internal/service/meta/meta_service.go
@@ -23,6 +23,7 @@ import (
 	"context"
 	"encoding/json"
 	"errors"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"strconv"
 	"strings"
 
@@ -46,14 +47,22 @@ type MetaService struct {
 	userCommon        *usercommon.UserCommon
 	questionRepo      questioncommon.QuestionRepo
 	answerRepo        answercommon.AnswerRepo
+	eventQueueService event_queue.EventQueueService
 }
 
-func NewMetaService(metaCommonService *metacommon.MetaCommonService, userCommon *usercommon.UserCommon, answerRepo answercommon.AnswerRepo, questionRepo questioncommon.QuestionRepo) *MetaService {
+func NewMetaService(
+	metaCommonService *metacommon.MetaCommonService,
+	userCommon *usercommon.UserCommon,
+	answerRepo answercommon.AnswerRepo,
+	questionRepo questioncommon.QuestionRepo,
+	eventQueueService event_queue.EventQueueService,
+) *MetaService {
 	return &MetaService{
 		metaCommonService: metaCommonService,
 		questionRepo:      questionRepo,
 		userCommon:        userCommon,
 		answerRepo:        answerRepo,
+		eventQueueService: eventQueueService,
 	}
 }
 
@@ -86,22 +95,27 @@ func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.Upda
 	if err != nil {
 		return nil, err
 	}
+	var event *schema.EventMsg
 	if objectType == constant.AnswerObjectType {
-		_, exist, err := ms.answerRepo.GetAnswer(ctx, req.ObjectID)
+		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).
+			AID(answerInfo.ID, answerInfo.UserID)
 	} else if objectType == constant.QuestionObjectType {
-		_, exist, err := ms.questionRepo.GetQuestion(ctx, req.ObjectID)
+		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).
+			QID(questionInfo.ID, questionInfo.UserID)
 	} else {
 		return nil, myErrors.BadRequest(reason.ObjectNotFound)
 	}
@@ -138,7 +152,7 @@ func (ms *MetaService) AddOrUpdateReaction(ctx context.Context, req *schema.Upda
 	if err != nil {
 		return nil, err
 	}
-
+	ms.eventQueueService.Send(ctx, event)
 	return resp, nil
 }
 
diff --git a/internal/service/provider.go b/internal/service/provider.go
index e82d9316..a22a6ea9 100644
--- a/internal/service/provider.go
+++ b/internal/service/provider.go
@@ -33,6 +33,7 @@ import (
 	"github.com/apache/incubator-answer/internal/service/config"
 	"github.com/apache/incubator-answer/internal/service/content"
 	"github.com/apache/incubator-answer/internal/service/dashboard"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 	"github.com/apache/incubator-answer/internal/service/export"
 	"github.com/apache/incubator-answer/internal/service/follow"
 	"github.com/apache/incubator-answer/internal/service/meta"
@@ -117,4 +118,5 @@ var ProviderSetService = wire.NewSet(
 	notice_queue.NewNewQuestionNotificationQueueService,
 	review.NewReviewService,
 	meta.NewMetaService,
+	event_queue.NewEventQueueService,
 )
diff --git a/internal/service/report/report_service.go b/internal/service/report/report_service.go
index 7f060a39..218423e1 100644
--- a/internal/service/report/report_service.go
+++ b/internal/service/report/report_service.go
@@ -21,6 +21,7 @@ package report
 
 import (
 	"encoding/json"
+	"github.com/apache/incubator-answer/internal/service/event_queue"
 
 	"github.com/apache/incubator-answer/internal/base/constant"
 	"github.com/apache/incubator-answer/internal/base/handler"
@@ -55,6 +56,7 @@ type ReportService struct {
 	commentCommonRepo comment_common.CommentCommonRepo
 	reportHandle      *report_handle.ReportHandle
 	configService     *config.ConfigService
+	eventQueueService event_queue.EventQueueService
 }
 
 // NewReportService new report service
@@ -67,6 +69,7 @@ func NewReportService(
 	commentCommonRepo comment_common.CommentCommonRepo,
 	reportHandle *report_handle.ReportHandle,
 	configService *config.ConfigService,
+	eventQueueService event_queue.EventQueueService,
 ) *ReportService {
 	return &ReportService{
 		reportRepo:        reportRepo,
@@ -77,6 +80,7 @@ func NewReportService(
 		commentCommonRepo: commentCommonRepo,
 		reportHandle:      reportHandle,
 		configService:     configService,
+		eventQueueService: eventQueueService,
 	}
 }
 
@@ -112,7 +116,12 @@ func (rs *ReportService) AddReport(ctx context.Context, req *schema.AddReportReq
 		Content:        req.Content,
 		Status:         entity.ReportStatusPending,
 	}
-	return rs.reportRepo.AddReport(ctx, report)
+	err = rs.reportRepo.AddReport(ctx, report)
+	if err != nil {
+		return err
+	}
+	rs.sendEvent(ctx, report, objInfo)
+	return nil
 }
 
 // GetUnreviewedReportPostPage get unreviewed report post page
@@ -218,3 +227,22 @@ func (rs *ReportService) ReviewReport(ctx context.Context, req *schema.ReviewRep
 
 	return rs.reportRepo.UpdateStatus(ctx, report.ID, entity.ReportStatusCompleted)
 }
+
+func (rs *ReportService) sendEvent(ctx context.Context,
+	report *entity.Report, objectInfo *schema.SimpleObjectInfo) {
+	var event *schema.EventMsg
+	switch objectInfo.ObjectType {
+	case constant.QuestionObjectType:
+		event = schema.NewEvent(constant.EventQuestionFlag, report.UserID).
+			QID(objectInfo.QuestionID, objectInfo.ObjectCreatorUserID)
+	case constant.AnswerObjectType:
+		event = schema.NewEvent(constant.EventAnswerFlag, report.UserID).
+			AID(objectInfo.AnswerID, objectInfo.ObjectCreatorUserID)
+	case constant.CommentObjectType:
+		event = schema.NewEvent(constant.EventCommentFlag, report.UserID).
+			CID(objectInfo.CommentID, objectInfo.ObjectCreatorUserID)
+	default:
+		return
+	}
+	rs.eventQueueService.Send(ctx, event)
+}