internal/repo/badge/badge_event_rule.go (195 lines of code) (raw):
/*
* 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/answer/internal/base/constant"
"github.com/apache/answer/internal/base/data"
"github.com/apache/answer/internal/base/reason"
"github.com/apache/answer/internal/entity"
"github.com/apache/answer/internal/schema"
"github.com/apache/answer/internal/service/badge"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
"strconv"
)
// eventRuleRepo event rule repo
type eventRuleRepo struct {
data *data.Data
EventRuleMapping map[constant.EventType][]badge.EventRuleHandler
}
// NewEventRuleRepo creates a new badge repository
func NewEventRuleRepo(data *data.Data) badge.EventRuleRepo {
b := &eventRuleRepo{
data: data,
}
b.EventRuleMapping = map[constant.EventType][]badge.EventRuleHandler{
constant.EventUserUpdate: {b.FirstUpdateUserProfile},
constant.EventUserShare: {b.FirstSharedPost},
constant.EventQuestionCreate: nil,
constant.EventQuestionUpdate: {b.FirstPostEdit},
constant.EventQuestionDelete: nil,
constant.EventQuestionVote: {b.FirstVotedPost, b.ReachQuestionVote},
constant.EventQuestionAccept: {b.FirstAcceptAnswer, b.ReachAnswerAcceptedAmount},
constant.EventQuestionFlag: {b.FirstFlaggedPost},
constant.EventQuestionReact: {b.FirstReactedPost},
constant.EventAnswerCreate: nil,
constant.EventAnswerUpdate: {b.FirstPostEdit},
constant.EventAnswerDelete: nil,
constant.EventAnswerVote: {b.FirstVotedPost, b.ReachAnswerVote},
constant.EventAnswerFlag: {b.FirstFlaggedPost},
constant.EventAnswerReact: {b.FirstReactedPost},
constant.EventCommentCreate: nil,
constant.EventCommentUpdate: nil,
constant.EventCommentDelete: nil,
constant.EventCommentVote: {b.FirstVotedPost},
constant.EventCommentFlag: {b.FirstFlaggedPost},
}
return b
}
// HandleEventWithRule handle event with rule
func (br *eventRuleRepo) HandleEventWithRule(ctx context.Context, msg *schema.EventMsg) (
awards []*entity.BadgeAward) {
handlers := br.EventRuleMapping[msg.EventType]
for _, handler := range handlers {
t, err := handler(ctx, msg)
if err != nil {
log.Errorf("error handling badge event %+v: %v", msg, err)
} else {
awards = append(awards, t...)
}
}
return awards
}
// FirstUpdateUserProfile first update user profile
func (br *eventRuleRepo) FirstUpdateUserProfile(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstUpdateUserProfile")
for _, b := range badges {
bean := &entity.User{ID: event.UserID}
exist, err := br.data.DB.Context(ctx).Get(bean)
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
if !exist {
continue
}
if len(bean.Bio) > 0 {
awards = append(awards, br.createBadgeAward(event.UserID, entity.BadgeEmptyAwardKey, b))
}
}
return awards, nil
}
// FirstPostEdit first post edit
func (br *eventRuleRepo) FirstPostEdit(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstPostEdit")
for _, b := range badges {
awards = append(awards, br.createBadgeAward(event.UserID, event.GetObjectID(), b))
}
return awards, nil
}
// FirstFlaggedPost first flagged post.
func (br *eventRuleRepo) FirstFlaggedPost(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstFlaggedPost")
for _, b := range badges {
awards = append(awards, br.createBadgeAward(event.UserID, event.GetObjectID(), b))
}
return awards, nil
}
// FirstVotedPost first voted post
func (br *eventRuleRepo) FirstVotedPost(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstVotedPost")
for _, b := range badges {
awards = append(awards, br.createBadgeAward(event.UserID, event.GetObjectID(), b))
}
return awards, nil
}
// FirstReactedPost first reacted post
func (br *eventRuleRepo) FirstReactedPost(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstReactedPost")
for _, b := range badges {
awards = append(awards, br.createBadgeAward(event.UserID, event.GetObjectID(), b))
}
return awards, nil
}
// FirstSharedPost first shared post
func (br *eventRuleRepo) FirstSharedPost(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstSharedPost")
for _, b := range badges {
awards = append(awards, br.createBadgeAward(event.UserID, event.GetObjectID(), b))
}
return awards, nil
}
// FirstAcceptAnswer user first accept answer
func (br *eventRuleRepo) FirstAcceptAnswer(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "FirstAcceptAnswer")
for _, b := range badges {
awards = append(awards, br.createBadgeAward(event.UserID, event.GetObjectID(), b))
}
return awards, nil
}
// ReachAnswerAcceptedAmount reach answer accepted amount
func (br *eventRuleRepo) ReachAnswerAcceptedAmount(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "ReachAnswerAcceptedAmount")
if len(event.AnswerUserID) == 0 {
return nil, nil
}
// count user's accepted answer amount
amount, err := br.data.DB.Context(ctx).Count(&entity.Answer{
UserID: event.AnswerUserID,
Accepted: schema.AnswerAcceptedEnable,
Status: entity.AnswerStatusAvailable,
})
if err != nil {
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
for _, b := range badges {
// get badge requirement
requirement := b.GetIntParam("amount")
if requirement == 0 || amount < requirement {
continue
}
awards = append(awards, br.createBadgeAward(event.AnswerUserID, event.AnswerID, b))
}
return awards, nil
}
// ReachAnswerVote reach answer vote
func (br *eventRuleRepo) ReachAnswerVote(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "ReachAnswerVote")
// get vote amount
amount, _ := strconv.Atoi(event.GetExtra("vote_up_amount"))
if amount == 0 {
return nil, nil
}
for _, b := range badges {
// get badge requirement
requirement := b.GetIntParam("amount")
if requirement == 0 || int64(amount) < requirement {
continue
}
awards = append(awards, br.createBadgeAward(event.AnswerUserID, event.AnswerID, b))
}
return awards, nil
}
// ReachQuestionVote reach question vote
func (br *eventRuleRepo) ReachQuestionVote(ctx context.Context,
event *schema.EventMsg) (awards []*entity.BadgeAward, err error) {
badges := br.getBadgesByHandler(ctx, "ReachQuestionVote")
// get vote amount
amount, _ := strconv.Atoi(event.GetExtra("vote_up_amount"))
if amount == 0 {
return nil, nil
}
for _, b := range badges {
// get badge requirement
requirement := b.GetIntParam("amount")
if requirement == 0 || int64(amount) < requirement {
continue
}
awards = append(awards, br.createBadgeAward(event.QuestionUserID, event.QuestionID, b))
}
return awards, nil
}
func (br *eventRuleRepo) getBadgesByHandler(ctx context.Context, handler string) (badges []*entity.Badge) {
badges = make([]*entity.Badge, 0)
err := br.data.DB.Context(ctx).Where("handler = ?", handler).Find(&badges)
if err != nil {
log.Errorf("error getting badge by handler %s: %v", handler, err)
return nil
}
return badges
}
func (br *eventRuleRepo) createBadgeAward(userID, awardKey string, badge *entity.Badge) (awards *entity.BadgeAward) {
return &entity.BadgeAward{
UserID: userID,
BadgeID: badge.ID,
AwardKey: awardKey,
}
}