mirror of https://gitee.com/answerdev/answer.git
refactor(votes): refactor user vote repo
This commit is contained in:
parent
9a31d7e76d
commit
47661dc8a3
|
@ -153,8 +153,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
reportRepo := report.NewReportRepo(dataData, uniqueIDRepo)
|
||||
reportService := report2.NewReportService(reportRepo, objService)
|
||||
reportController := controller.NewReportController(reportService, rankService)
|
||||
serviceVoteRepo := activity.NewVoteRepo(dataData, uniqueIDRepo, configService, activityRepo, userRankRepo, voteRepo, notificationQueueService)
|
||||
voteService := service.NewVoteService(serviceVoteRepo, uniqueIDRepo, configService, questionRepo, answerRepo, commentCommonRepo, objService)
|
||||
serviceVoteRepo := activity.NewVoteRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
|
||||
voteService := service.NewVoteService(serviceVoteRepo, configService, questionRepo, answerRepo, commentCommonRepo, objService)
|
||||
voteController := controller.NewVoteController(voteService, rankService)
|
||||
followRepo := activity_common.NewFollowRepo(dataData, uniqueIDRepo, activityRepo)
|
||||
tagService := tag2.NewTagService(tagRepo, tagCommonService, revisionService, followRepo, siteInfoCommonService, activityQueueService)
|
||||
|
@ -172,8 +172,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
collectionService := service.NewCollectionService(collectionRepo, collectionGroupRepo, questionCommon)
|
||||
collectionController := controller.NewCollectionController(collectionService)
|
||||
answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
|
||||
questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo)
|
||||
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo)
|
||||
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo)
|
||||
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, userRepo, revisionService, metaService, collectionCommon, answerActivityService, emailService, notificationQueueService, activityQueueService, siteInfoCommonService)
|
||||
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService, notificationQueueService, activityQueueService)
|
||||
questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService)
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/rank"
|
||||
"github.com/answerdev/answer/pkg/uid"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
|
@ -54,9 +53,7 @@ func (vc *VoteController) VoteUp(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
dto := &schema.VoteDTO{}
|
||||
_ = copier.Copy(dto, req)
|
||||
resp, err := vc.VoteService.VoteUp(ctx, dto)
|
||||
resp, err := vc.VoteService.VoteUp(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
||||
} else {
|
||||
|
@ -93,9 +90,7 @@ func (vc *VoteController) VoteDown(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
dto := &schema.VoteDTO{}
|
||||
_ = copier.Copy(dto, req)
|
||||
resp, err := vc.VoteService.VoteDown(ctx, dto)
|
||||
resp, err := vc.VoteService.VoteDown(ctx, req)
|
||||
if err != nil {
|
||||
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
||||
} else {
|
||||
|
|
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/rank"
|
||||
"github.com/answerdev/answer/pkg/converter"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
|
@ -46,79 +45,6 @@ func NewAnswerActivityRepo(
|
|||
}
|
||||
}
|
||||
|
||||
// NewQuestionActivityRepo new repository
|
||||
func NewQuestionActivityRepo(
|
||||
data *data.Data,
|
||||
activityRepo activity_common.ActivityRepo,
|
||||
userRankRepo rank.UserRankRepo,
|
||||
) activity.QuestionActivityRepo {
|
||||
return &AnswerActivityRepo{
|
||||
data: data,
|
||||
activityRepo: activityRepo,
|
||||
userRankRepo: userRankRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (ar *AnswerActivityRepo) DeleteQuestion(ctx context.Context, questionID string) (err error) {
|
||||
questionInfo := &entity.Question{}
|
||||
exist, err := ar.data.DB.Context(ctx).Where("id = ?", questionID).Get(questionInfo)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get all this object activity
|
||||
activityList := make([]*entity.Activity, 0)
|
||||
session := ar.data.DB.Context(ctx).Where("has_rank = 1")
|
||||
session.Where("cancelled = ?", entity.ActivityAvailable)
|
||||
err = session.Find(&activityList, &entity.Activity{ObjectID: questionID})
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if len(activityList) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("questionInfo %s deleted will rollback activity %d", questionID, len(activityList))
|
||||
|
||||
_, err = ar.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||
session = session.Context(ctx)
|
||||
for _, act := range activityList {
|
||||
log.Infof("user %s rollback rank %d", act.UserID, -act.Rank)
|
||||
_, e := ar.userRankRepo.TriggerUserRank(
|
||||
ctx, session, act.UserID, -act.Rank, act.ActivityType)
|
||||
if e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
|
||||
if _, e := session.Where("id = ?", act.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled, CancelledAt: time.Now()}); e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// get all answers
|
||||
answerList := make([]*entity.Answer, 0)
|
||||
err = ar.data.DB.Context(ctx).Find(&answerList, &entity.Answer{QuestionID: questionID})
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
for _, answerInfo := range answerList {
|
||||
err = ar.DeleteAnswer(ctx, answerInfo.ID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AcceptAnswer accept other answer
|
||||
func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool,
|
||||
|
@ -306,50 +232,3 @@ func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
|
|||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (ar *AnswerActivityRepo) DeleteAnswer(ctx context.Context, answerID string) (err error) {
|
||||
answerInfo := &entity.Answer{}
|
||||
exist, err := ar.data.DB.Context(ctx).Where("id = ?", answerID).Get(answerInfo)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
|
||||
// get all this object activity
|
||||
activityList := make([]*entity.Activity, 0)
|
||||
session := ar.data.DB.Context(ctx).Where("has_rank = 1")
|
||||
session.Where("cancelled = ?", entity.ActivityAvailable)
|
||||
err = session.Find(&activityList, &entity.Activity{ObjectID: answerID})
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if len(activityList) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("answerInfo %s deleted will rollback activity %d", answerID, len(activityList))
|
||||
|
||||
_, err = ar.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||
session = session.Context(ctx)
|
||||
for _, act := range activityList {
|
||||
log.Infof("user %s rollback rank %d", act.UserID, -act.Rank)
|
||||
_, e := ar.userRankRepo.TriggerUserRank(
|
||||
ctx, session, act.UserID, -act.Rank, act.ActivityType)
|
||||
if e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
|
||||
if _, e := session.Where("id = ?", act.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityCancelled, CancelledAt: time.Now()}); e != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(e).WithStack()
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func (ar *FollowRepo) Follow(ctx context.Context, objectID, userID string) error
|
|||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectTypeStr, "follow")
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectTypeStr, "follow")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ func (ar *FollowRepo) FollowCancel(ctx context.Context, objectID, userID string)
|
|||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectTypeStr, "follow")
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectTypeStr, "follow")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package activity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"xorm.io/builder"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
|
@ -41,43 +43,58 @@ func NewUserActiveActivityRepo(
|
|||
}
|
||||
}
|
||||
|
||||
// UserActive accept other answer
|
||||
// UserActive user active
|
||||
func (ar *UserActiveActivityRepo) UserActive(ctx context.Context, userID string) (err error) {
|
||||
cfg, err := ar.configService.GetConfigByKey(ctx, UserActivated)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
activityType := cfg.ID
|
||||
deltaRank := cfg.GetIntValue()
|
||||
addActivity := &entity.Activity{
|
||||
UserID: userID,
|
||||
ObjectID: "0",
|
||||
OriginalObjectID: "0",
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
ActivityType: cfg.ID,
|
||||
Rank: cfg.GetIntValue(),
|
||||
HasRank: 1,
|
||||
}
|
||||
|
||||
_, err = ar.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||
session = session.Context(ctx)
|
||||
|
||||
_, exists, err := ar.activityRepo.GetActivity(ctx, session, "0", addActivity.UserID, activityType)
|
||||
user := &entity.User{}
|
||||
exist, err := session.ID(userID).ForUpdate().Get(user)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
return nil, err
|
||||
}
|
||||
if exists {
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("user not exist")
|
||||
}
|
||||
|
||||
existsActivity := &entity.Activity{}
|
||||
exist, err = session.
|
||||
And(builder.Eq{"user_id": addActivity.UserID}).
|
||||
And(builder.Eq{"activity_type": addActivity.ActivityType}).
|
||||
Get(existsActivity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_, err = ar.userRankRepo.TriggerUserRank(ctx, session, addActivity.UserID, addActivity.Rank, activityType)
|
||||
err = ar.userRankRepo.ChangeUserRank(ctx, session, addActivity.UserID, user.Rank, addActivity.Rank)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = session.Insert(addActivity)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
return err
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@ package activity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"fmt"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
|
@ -10,20 +11,17 @@ import (
|
|||
"github.com/answerdev/answer/pkg/converter"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/pager"
|
||||
"github.com/answerdev/answer/internal/service/config"
|
||||
"github.com/answerdev/answer/internal/service/rank"
|
||||
"github.com/answerdev/answer/pkg/obj"
|
||||
|
||||
"xorm.io/builder"
|
||||
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/unique"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
@ -31,365 +29,147 @@ import (
|
|||
// VoteRepo activity repository
|
||||
type VoteRepo struct {
|
||||
data *data.Data
|
||||
uniqueIDRepo unique.UniqueIDRepo
|
||||
configService *config.ConfigService
|
||||
activityRepo activity_common.ActivityRepo
|
||||
userRankRepo rank.UserRankRepo
|
||||
voteCommon activity_common.VoteRepo
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
}
|
||||
|
||||
// NewVoteRepo new repository
|
||||
func NewVoteRepo(
|
||||
data *data.Data,
|
||||
uniqueIDRepo unique.UniqueIDRepo,
|
||||
configService *config.ConfigService,
|
||||
activityRepo activity_common.ActivityRepo,
|
||||
userRankRepo rank.UserRankRepo,
|
||||
voteCommon activity_common.VoteRepo,
|
||||
notificationQueueService notice_queue.NotificationQueueService,
|
||||
) service.VoteRepo {
|
||||
return &VoteRepo{
|
||||
data: data,
|
||||
uniqueIDRepo: uniqueIDRepo,
|
||||
configService: configService,
|
||||
activityRepo: activityRepo,
|
||||
userRankRepo: userRankRepo,
|
||||
voteCommon: voteCommon,
|
||||
notificationQueueService: notificationQueueService,
|
||||
}
|
||||
}
|
||||
|
||||
var LimitUpActions = map[string][]string{
|
||||
"question": {"vote_up", "voted_up"},
|
||||
"answer": {"vote_up", "voted_up"},
|
||||
"comment": {"vote_up"},
|
||||
}
|
||||
|
||||
var LimitDownActions = map[string][]string{
|
||||
"question": {"vote_down", "voted_down"},
|
||||
"answer": {"vote_down", "voted_down"},
|
||||
"comment": {"vote_down"},
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) vote(ctx context.Context, objectID string, userID, objectUserID string, actions []string) (resp *schema.VoteResp, err error) {
|
||||
resp = &schema.VoteResp{}
|
||||
achievementNotificationUserIDs := make([]string, 0)
|
||||
sendInboxNotification := false
|
||||
upVote := false
|
||||
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||
session = session.Context(ctx)
|
||||
result = nil
|
||||
for _, action := range actions {
|
||||
var (
|
||||
existsActivity entity.Activity
|
||||
insertActivity entity.Activity
|
||||
has bool
|
||||
triggerUserID,
|
||||
activityUserID string
|
||||
activityType, deltaRank, hasRank int
|
||||
)
|
||||
|
||||
activityUserID, activityType, deltaRank, hasRank, err = vr.CheckRank(ctx, objectID, objectUserID, userID, action)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
triggerUserID = userID
|
||||
if userID == activityUserID {
|
||||
triggerUserID = "0"
|
||||
}
|
||||
|
||||
// check is voted up
|
||||
has, _ = session.
|
||||
Where(builder.Eq{"object_id": objectID}).
|
||||
And(builder.Eq{"user_id": activityUserID}).
|
||||
And(builder.Eq{"trigger_user_id": triggerUserID}).
|
||||
And(builder.Eq{"activity_type": activityType}).
|
||||
Get(&existsActivity)
|
||||
|
||||
// is is voted,return
|
||||
if has && existsActivity.Cancelled == entity.ActivityAvailable {
|
||||
return
|
||||
}
|
||||
|
||||
insertActivity = entity.Activity{
|
||||
ObjectID: objectID,
|
||||
OriginalObjectID: objectID,
|
||||
UserID: activityUserID,
|
||||
TriggerUserID: converter.StringToInt64(triggerUserID),
|
||||
ActivityType: activityType,
|
||||
Rank: deltaRank,
|
||||
HasRank: hasRank,
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
}
|
||||
|
||||
// trigger user rank and send notification
|
||||
if hasRank != 0 {
|
||||
var isReachStandard bool
|
||||
isReachStandard, err = vr.userRankRepo.TriggerUserRank(ctx, session, activityUserID, deltaRank, activityType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isReachStandard {
|
||||
insertActivity.Rank = 0
|
||||
}
|
||||
achievementNotificationUserIDs = append(achievementNotificationUserIDs, activityUserID)
|
||||
}
|
||||
|
||||
if has {
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
|
||||
Update(&entity.Activity{
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = session.Insert(&insertActivity)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sendInboxNotification = true
|
||||
}
|
||||
|
||||
// update votes
|
||||
if action == constant.ActVoteDown || action == constant.ActVoteUp {
|
||||
votes := 1
|
||||
if action == constant.ActVoteDown {
|
||||
upVote = false
|
||||
votes = -1
|
||||
} else {
|
||||
upVote = true
|
||||
}
|
||||
err = vr.updateVotes(ctx, session, objectID, votes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
func (vr *VoteRepo) Vote(ctx context.Context, op *schema.VoteOperationInfo) (err error) {
|
||||
noNeedToVote, err := vr.votePreCheck(ctx, op)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
if noNeedToVote {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err = vr.GetVoteResultByObjectId(ctx, objectID)
|
||||
resp.VoteStatus = vr.voteCommon.GetVoteStatus(ctx, objectID, userID)
|
||||
sendInboxNotification := false
|
||||
maxDailyRank, err := vr.userRankRepo.GetMaxDailyRank(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var userIDs []string
|
||||
for _, activity := range op.Activities {
|
||||
userIDs = append(userIDs, activity.ActivityUserID)
|
||||
}
|
||||
|
||||
for _, activityUserID := range achievementNotificationUserIDs {
|
||||
vr.sendNotification(ctx, activityUserID, objectUserID, objectID)
|
||||
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||
session = session.Context(ctx)
|
||||
|
||||
userInfoMapping, err := vr.acquireUserInfo(session, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = vr.setActivityRankToZeroIfUserReachLimit(ctx, session, op, maxDailyRank)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sendInboxNotification, err = vr.saveActivitiesAvailable(session, op)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = vr.changeUserRank(ctx, session, op, userInfoMapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, activity := range op.Activities {
|
||||
if activity.Rank == 0 {
|
||||
continue
|
||||
}
|
||||
vr.sendAchievementNotification(ctx, activity.ActivityUserID, op.ObjectCreatorUserID, op.ObjectID)
|
||||
}
|
||||
if sendInboxNotification {
|
||||
vr.sendVoteInboxNotification(ctx, userID, objectUserID, objectID, upVote)
|
||||
vr.sendVoteInboxNotification(ctx, op.OperatingUserID, op.ObjectCreatorUserID, op.ObjectID, op.VoteUp)
|
||||
}
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) voteCancel(ctx context.Context, objectID string, userID, objectUserID string, actions []string) (resp *schema.VoteResp, err error) {
|
||||
resp = &schema.VoteResp{}
|
||||
notificationUserIDs := make([]string, 0)
|
||||
func (vr *VoteRepo) CancelVote(ctx context.Context, op *schema.VoteOperationInfo) (err error) {
|
||||
// Pre-Check
|
||||
// 1. check if the activity exist
|
||||
// 2. check if the activity is not cancelled
|
||||
// 3. if all activities are cancelled, return directly
|
||||
activities, err := vr.getExistActivity(ctx, op)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var userIDs []string
|
||||
for _, activity := range activities {
|
||||
if activity.Cancelled == entity.ActivityCancelled {
|
||||
continue
|
||||
}
|
||||
userIDs = append(userIDs, activity.UserID)
|
||||
}
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||
session = session.Context(ctx)
|
||||
for _, action := range actions {
|
||||
var (
|
||||
existsActivity entity.Activity
|
||||
has bool
|
||||
triggerUserID,
|
||||
activityUserID string
|
||||
activityType,
|
||||
deltaRank, hasRank int
|
||||
)
|
||||
result = nil
|
||||
|
||||
activityUserID, activityType, deltaRank, hasRank, err = vr.CheckRank(ctx, objectID, objectUserID, userID, action)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
triggerUserID = userID
|
||||
if userID == activityUserID {
|
||||
triggerUserID = "0"
|
||||
}
|
||||
|
||||
has, err = session.
|
||||
Where(builder.Eq{"user_id": activityUserID}).
|
||||
And(builder.Eq{"trigger_user_id": triggerUserID}).
|
||||
And(builder.Eq{"activity_type": activityType}).
|
||||
And(builder.Eq{"object_id": objectID}).
|
||||
Get(&existsActivity)
|
||||
|
||||
if !has {
|
||||
return
|
||||
}
|
||||
|
||||
if existsActivity.Cancelled == entity.ActivityCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{
|
||||
Cancelled: entity.ActivityCancelled,
|
||||
CancelledAt: time.Now(),
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// trigger user rank and send notification
|
||||
if hasRank != 0 && existsActivity.Rank != 0 {
|
||||
_, err = vr.userRankRepo.TriggerUserRank(ctx, session, activityUserID, -deltaRank, activityType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
notificationUserIDs = append(notificationUserIDs, activityUserID)
|
||||
}
|
||||
|
||||
// update votes
|
||||
if action == "vote_down" || action == "vote_up" {
|
||||
votes := -1
|
||||
if action == "vote_down" {
|
||||
votes = 1
|
||||
}
|
||||
err = vr.updateVotes(ctx, session, objectID, votes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
userInfoMapping, err := vr.acquireUserInfo(session, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return
|
||||
err = vr.cancelActivities(session, activities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = vr.rollbackUserRank(ctx, session, activities, userInfoMapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
resp, err = vr.GetVoteResultByObjectId(ctx, objectID)
|
||||
resp.VoteStatus = vr.voteCommon.GetVoteStatus(ctx, objectID, userID)
|
||||
|
||||
for _, activityUserID := range notificationUserIDs {
|
||||
vr.sendNotification(ctx, activityUserID, objectUserID, objectID)
|
||||
for _, activity := range activities {
|
||||
if activity.Rank == 0 {
|
||||
continue
|
||||
}
|
||||
vr.sendAchievementNotification(ctx, activity.UserID, op.ObjectCreatorUserID, op.ObjectID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) GetAndSaveVoteResult(ctx context.Context, objectID, objectType string) (
|
||||
up, down int64, err error) {
|
||||
up = vr.countVoteUp(ctx, objectID, objectType)
|
||||
down = vr.countVoteDown(ctx, objectID, objectType)
|
||||
err = vr.updateVotes(ctx, objectID, objectType, int(up-down))
|
||||
return
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) VoteUp(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
|
||||
resp = &schema.VoteResp{}
|
||||
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
err = errors.BadRequest(reason.ObjectNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
actions, ok := LimitUpActions[objectType]
|
||||
if !ok {
|
||||
err = errors.BadRequest(reason.DisallowVote)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = vr.VoteDownCancel(ctx, objectID, userID, objectUserID)
|
||||
return vr.vote(ctx, objectID, userID, objectUserID, actions)
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) VoteDown(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
|
||||
resp = &schema.VoteResp{}
|
||||
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
err = errors.BadRequest(reason.ObjectNotFound)
|
||||
return
|
||||
}
|
||||
actions, ok := LimitDownActions[objectType]
|
||||
if !ok {
|
||||
err = errors.BadRequest(reason.DisallowVote)
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = vr.VoteUpCancel(ctx, objectID, userID, objectUserID)
|
||||
return vr.vote(ctx, objectID, userID, objectUserID, actions)
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) VoteUpCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
|
||||
var objectType string
|
||||
resp = &schema.VoteResp{}
|
||||
|
||||
objectType, err = obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
err = errors.BadRequest(reason.ObjectNotFound)
|
||||
return
|
||||
}
|
||||
actions, ok := LimitUpActions[objectType]
|
||||
if !ok {
|
||||
err = errors.BadRequest(reason.DisallowVote)
|
||||
return
|
||||
}
|
||||
|
||||
return vr.voteCancel(ctx, objectID, userID, objectUserID, actions)
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) VoteDownCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error) {
|
||||
var objectType string
|
||||
resp = &schema.VoteResp{}
|
||||
|
||||
objectType, err = obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
err = errors.BadRequest(reason.ObjectNotFound)
|
||||
return
|
||||
}
|
||||
actions, ok := LimitDownActions[objectType]
|
||||
if !ok {
|
||||
err = errors.BadRequest(reason.DisallowVote)
|
||||
return
|
||||
}
|
||||
|
||||
return vr.voteCancel(ctx, objectID, userID, objectUserID, actions)
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) CheckRank(ctx context.Context, objectID, objectUserID, userID string, action string) (activityUserID string, activityType, rank, hasRank int, err error) {
|
||||
activityType, rank, hasRank, err = vr.activityRepo.GetActivityTypeByObjID(ctx, objectID, action)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
activityUserID = userID
|
||||
if strings.Contains(action, "voted") {
|
||||
activityUserID = objectUserID
|
||||
}
|
||||
|
||||
return activityUserID, activityType, rank, hasRank, nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) GetVoteResultByObjectId(ctx context.Context, objectID string) (resp *schema.VoteResp, err error) {
|
||||
resp = &schema.VoteResp{}
|
||||
for _, action := range []string{"vote_up", "vote_down"} {
|
||||
var (
|
||||
activity entity.Activity
|
||||
votes int64
|
||||
activityType int
|
||||
)
|
||||
|
||||
activityType, _, _, _ = vr.activityRepo.GetActivityTypeByObjID(ctx, objectID, action)
|
||||
|
||||
votes, err = vr.data.DB.Context(ctx).Where(builder.Eq{"object_id": objectID}).
|
||||
And(builder.Eq{"activity_type": activityType}).
|
||||
And(builder.Eq{"cancelled": 0}).
|
||||
Count(&activity)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if action == "vote_up" {
|
||||
resp.UpVotes = int(votes)
|
||||
} else {
|
||||
resp.DownVotes = int(votes)
|
||||
}
|
||||
}
|
||||
|
||||
resp.Votes = resp.UpVotes - resp.DownVotes
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) ListUserVotes(ctx context.Context, userID string,
|
||||
page int, pageSize int, activityTypes []int) (voteList []entity.Activity, total int64, err error) {
|
||||
page int, pageSize int, activityTypes []int) (voteList []*entity.Activity, total int64, err error) {
|
||||
session := vr.data.DB.Context(ctx)
|
||||
cond := builder.
|
||||
And(
|
||||
|
@ -407,37 +187,234 @@ func (vr *VoteRepo) ListUserVotes(ctx context.Context, userID string,
|
|||
return
|
||||
}
|
||||
|
||||
// updateVotes
|
||||
// if votes < 0 Decr object vote_count,otherwise Incr object vote_count
|
||||
func (vr *VoteRepo) updateVotes(ctx context.Context, session *xorm.Session, objectID string, votes int) (err error) {
|
||||
var (
|
||||
objectType string
|
||||
e error
|
||||
)
|
||||
func (vr *VoteRepo) votePreCheck(ctx context.Context, op *schema.VoteOperationInfo) (noNeedToVote bool, err error) {
|
||||
activities, err := vr.getExistActivity(ctx, op)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
done := 0
|
||||
for _, activity := range activities {
|
||||
if activity.Cancelled == entity.ActivityAvailable {
|
||||
done++
|
||||
}
|
||||
}
|
||||
return done == len(op.Activities), nil
|
||||
}
|
||||
|
||||
objectType, err = obj.GetObjectTypeStrByObjectID(objectID)
|
||||
func (vr *VoteRepo) acquireUserInfo(session *xorm.Session, userIDs []string) (map[string]*entity.User, error) {
|
||||
us := make([]*entity.User, 0)
|
||||
err := session.In("id", userIDs).ForUpdate().Find(&us)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := make(map[string]*entity.User, 0)
|
||||
for _, u := range us {
|
||||
users[u.ID] = u
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) setActivityRankToZeroIfUserReachLimit(ctx context.Context, session *xorm.Session,
|
||||
op *schema.VoteOperationInfo, maxDailyRank int) (err error) {
|
||||
// check if user reach daily rank limit
|
||||
for _, activity := range op.Activities {
|
||||
reach, err := vr.userRankRepo.CheckReachLimit(ctx, session, activity.ActivityUserID, maxDailyRank)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
if reach {
|
||||
activity.Rank = 0
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) changeUserRank(ctx context.Context, session *xorm.Session,
|
||||
op *schema.VoteOperationInfo,
|
||||
userInfoMapping map[string]*entity.User) (err error) {
|
||||
for _, activity := range op.Activities {
|
||||
if activity.Rank == 0 {
|
||||
continue
|
||||
}
|
||||
user := userInfoMapping[activity.ActivityUserID]
|
||||
if user == nil {
|
||||
continue
|
||||
}
|
||||
if err = vr.userRankRepo.ChangeUserRank(ctx, session,
|
||||
activity.ActivityUserID, user.Rank, activity.Rank); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) rollbackUserRank(ctx context.Context, session *xorm.Session,
|
||||
activities []*entity.Activity,
|
||||
userInfoMapping map[string]*entity.User) (err error) {
|
||||
for _, activity := range activities {
|
||||
if activity.Rank == 0 {
|
||||
continue
|
||||
}
|
||||
user := userInfoMapping[activity.UserID]
|
||||
if user == nil {
|
||||
continue
|
||||
}
|
||||
if err = vr.userRankRepo.ChangeUserRank(ctx, session,
|
||||
activity.UserID, user.Rank, -activity.Rank); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveActivitiesAvailable save activities
|
||||
// If activity not exist it will be created or else will be updated
|
||||
// If this activity is already exist, set activity rank to 0
|
||||
// So after this function, the activity rank will be correct for update user rank
|
||||
func (vr *VoteRepo) saveActivitiesAvailable(session *xorm.Session, op *schema.VoteOperationInfo) (newAct bool, err error) {
|
||||
for _, activity := range op.Activities {
|
||||
existsActivity := &entity.Activity{}
|
||||
exist, err := session.
|
||||
Where(builder.Eq{"object_id": op.ObjectID}).
|
||||
And(builder.Eq{"user_id": activity.ActivityUserID}).
|
||||
And(builder.Eq{"trigger_user_id": activity.TriggerUserID}).
|
||||
And(builder.Eq{"activity_type": activity.ActivityType}).
|
||||
Get(existsActivity)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if exist && existsActivity.Cancelled == entity.ActivityAvailable {
|
||||
activity.Rank = 0
|
||||
continue
|
||||
}
|
||||
if exist {
|
||||
if _, err = session.Where("id = ?", existsActivity.ID).Cols("`cancelled`").
|
||||
Update(&entity.Activity{Cancelled: entity.ActivityAvailable}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
insertActivity := entity.Activity{
|
||||
ObjectID: op.ObjectID,
|
||||
OriginalObjectID: op.ObjectID,
|
||||
UserID: activity.ActivityUserID,
|
||||
TriggerUserID: converter.StringToInt64(activity.TriggerUserID),
|
||||
ActivityType: activity.ActivityType,
|
||||
Rank: activity.Rank,
|
||||
HasRank: activity.HasRank(),
|
||||
Cancelled: entity.ActivityAvailable,
|
||||
}
|
||||
_, err = session.Insert(&insertActivity)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
newAct = true
|
||||
}
|
||||
}
|
||||
return newAct, nil
|
||||
}
|
||||
|
||||
// cancelActivities cancel activities
|
||||
// If this activity is already cancelled, set activity rank to 0
|
||||
// So after this function, the activity rank will be correct for update user rank
|
||||
func (vr *VoteRepo) cancelActivities(session *xorm.Session, activities []*entity.Activity) (err error) {
|
||||
for _, activity := range activities {
|
||||
t := &entity.Activity{}
|
||||
exist, err := session.ID(activity.ID).Get(t)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
log.Error(fmt.Errorf("%s activity not exist", activity.ID))
|
||||
return fmt.Errorf("%s activity not exist", activity.ID)
|
||||
}
|
||||
// If this activity is already cancelled, set activity rank to 0
|
||||
if t.Cancelled == entity.ActivityCancelled {
|
||||
activity.Rank = 0
|
||||
}
|
||||
if _, err = session.ID(activity.ID).Cols("cancelled", "cancelled_at").
|
||||
Update(&entity.Activity{
|
||||
Cancelled: entity.ActivityCancelled,
|
||||
CancelledAt: time.Now(),
|
||||
}); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) getExistActivity(ctx context.Context, op *schema.VoteOperationInfo) ([]*entity.Activity, error) {
|
||||
var activities []*entity.Activity
|
||||
for _, action := range op.Activities {
|
||||
t := &entity.Activity{}
|
||||
exist, err := vr.data.DB.Context(ctx).
|
||||
Where(builder.Eq{"user_id": action.ActivityUserID}).
|
||||
And(builder.Eq{"trigger_user_id": action.TriggerUserID}).
|
||||
And(builder.Eq{"activity_type": action.ActivityType}).
|
||||
And(builder.Eq{"object_id": op.ObjectID}).
|
||||
Get(t)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if exist {
|
||||
activities = append(activities, t)
|
||||
}
|
||||
}
|
||||
return activities, nil
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) countVoteUp(ctx context.Context, objectID, objectType string) (count int64) {
|
||||
count, err := vr.countVote(ctx, objectID, objectType, constant.ActVoteUp)
|
||||
if err != nil {
|
||||
log.Errorf("get vote up count error: %v", err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) countVoteDown(ctx context.Context, objectID, objectType string) (count int64) {
|
||||
count, err := vr.countVote(ctx, objectID, objectType, constant.ActVoteDown)
|
||||
if err != nil {
|
||||
log.Errorf("get vote down count error: %v", err)
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) countVote(ctx context.Context, objectID, objectType, action string) (count int64, err error) {
|
||||
activity := &entity.Activity{}
|
||||
activityType, _ := vr.activityRepo.GetActivityTypeByObjectType(ctx, objectType, action)
|
||||
count, err = vr.data.DB.Context(ctx).Where(builder.Eq{"object_id": objectID}).
|
||||
And(builder.Eq{"activity_type": activityType}).
|
||||
And(builder.Eq{"cancelled": 0}).
|
||||
Count(activity)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return count, err
|
||||
}
|
||||
|
||||
func (vr *VoteRepo) updateVotes(ctx context.Context, objectID, objectType string, voteCount int) (err error) {
|
||||
session := vr.data.DB.Context(ctx)
|
||||
switch objectType {
|
||||
case "question":
|
||||
_, err = session.Where("id = ?", objectID).Incr("vote_count", votes).Update(&entity.Question{})
|
||||
case "answer":
|
||||
_, err = session.Where("id = ?", objectID).Incr("vote_count", votes).Update(&entity.Answer{})
|
||||
case "comment":
|
||||
_, err = session.Where("id = ?", objectID).Incr("vote_count", votes).Update(&entity.Comment{})
|
||||
default:
|
||||
e = errors.BadRequest(reason.DisallowVote)
|
||||
case constant.QuestionObjectType:
|
||||
_, err = session.ID(objectID).Cols("vote_count").Update(&entity.Question{VoteCount: voteCount})
|
||||
case constant.AnswerObjectType:
|
||||
_, err = session.ID(objectID).Cols("vote_count").Update(&entity.Answer{VoteCount: voteCount})
|
||||
case constant.CommentObjectType:
|
||||
_, err = session.ID(objectID).Cols("vote_count").Update(&entity.Comment{VoteCount: voteCount})
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
err = e
|
||||
} else if err != nil {
|
||||
err = errors.BadRequest(reason.DatabaseError).WithError(err).WithStack()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// sendNotification send rank triggered notification
|
||||
func (vr *VoteRepo) sendNotification(ctx context.Context, activityUserID, objectUserID, objectID string) {
|
||||
func (vr *VoteRepo) sendAchievementNotification(ctx context.Context, activityUserID, objectUserID, objectID string) {
|
||||
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
return
|
||||
|
|
|
@ -41,12 +41,12 @@ func NewActivityRepo(
|
|||
|
||||
func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectID string, action string) (
|
||||
activityType, rank, hasRank int, err error) {
|
||||
objectKey, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
confKey := fmt.Sprintf("%s.%s", objectKey, action)
|
||||
confKey := fmt.Sprintf("%s.%s", objectType, action)
|
||||
cfg, err := ar.configService.GetConfigByKey(ctx, confKey)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -59,8 +59,8 @@ func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectID str
|
|||
return
|
||||
}
|
||||
|
||||
func (ar *ActivityRepo) GetActivityTypeByObjKey(ctx context.Context, objectKey, action string) (activityType int, err error) {
|
||||
configKey := fmt.Sprintf("%s.%s", objectKey, action)
|
||||
func (ar *ActivityRepo) GetActivityTypeByObjectType(ctx context.Context, objectType, action string) (activityType int, err error) {
|
||||
configKey := fmt.Sprintf("%s.%s", objectType, action)
|
||||
cfg, err := ar.configService.GetConfigByKey(ctx, configKey)
|
||||
if err != nil {
|
||||
return 0, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
|
|
|
@ -74,7 +74,7 @@ func (ar *FollowRepo) GetFollowUserIDs(ctx context.Context, objectID string) (us
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectTypeStr, "follow")
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectTypeStr, "follow")
|
||||
if err != nil {
|
||||
log.Errorf("can't get activity type by object key: %s", objectTypeStr)
|
||||
return nil, err
|
||||
|
@ -96,7 +96,7 @@ func (ar *FollowRepo) GetFollowUserIDs(ctx context.Context, objectID string) (us
|
|||
// GetFollowIDs get all follow id list
|
||||
func (ar *FollowRepo) GetFollowIDs(ctx context.Context, userID, objectKey string) (followIDs []string, err error) {
|
||||
followIDs = make([]string, 0)
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectKey, "follow")
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectKey, "follow")
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ func (ar *FollowRepo) IsFollowed(ctx context.Context, userID, objectID string) (
|
|||
return false, err
|
||||
}
|
||||
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectKey, "follow")
|
||||
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectKey, "follow")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -52,7 +52,6 @@ var ProviderSetRepo = wire.NewSet(
|
|||
activity.NewVoteRepo,
|
||||
activity.NewFollowRepo,
|
||||
activity.NewAnswerActivityRepo,
|
||||
activity.NewQuestionActivityRepo,
|
||||
activity.NewUserActiveActivityRepo,
|
||||
activity.NewActivityRepo,
|
||||
tag.NewTagRepo,
|
||||
|
|
|
@ -31,6 +31,56 @@ func NewUserRankRepo(data *data.Data, configService *config.ConfigService) rank.
|
|||
}
|
||||
}
|
||||
|
||||
func (ur *UserRankRepo) GetMaxDailyRank(ctx context.Context) (maxDailyRank int, err error) {
|
||||
maxDailyRank, err = ur.configService.GetIntValue(ctx, "daily_rank_limit")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return maxDailyRank, nil
|
||||
}
|
||||
|
||||
func (ur *UserRankRepo) CheckReachLimit(ctx context.Context, session *xorm.Session,
|
||||
userID string, maxDailyRank int) (
|
||||
reach bool, err error) {
|
||||
session.Where(builder.Eq{"user_id": userID})
|
||||
session.Where(builder.Eq{"cancelled": 0})
|
||||
session.Where(builder.Between{
|
||||
Col: "updated_at",
|
||||
LessVal: now.BeginningOfDay(),
|
||||
MoreVal: now.EndOfDay(),
|
||||
})
|
||||
|
||||
earned, err := session.Sum(&entity.Activity{}, "`rank`")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if int(earned) <= maxDailyRank {
|
||||
return false, nil
|
||||
}
|
||||
log.Infof("user %s today has rank %d is reach stand %d", userID, earned, maxDailyRank)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// ChangeUserRank change user rank
|
||||
func (ur *UserRankRepo) ChangeUserRank(
|
||||
ctx context.Context, session *xorm.Session, userID string, userCurrentScore, deltaRank int) (err error) {
|
||||
// IMPORTANT: If user center enabled the rank agent, then we should not change user rank.
|
||||
if plugin.RankAgentEnabled() || deltaRank == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If user rank is lower than 1 after this action, then user rank will be set to 1 only.
|
||||
if deltaRank < 0 && userCurrentScore+deltaRank < 1 {
|
||||
deltaRank = 1 - userCurrentScore
|
||||
}
|
||||
|
||||
_, err = session.ID(userID).Incr("`rank`", deltaRank).Update(&entity.User{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TriggerUserRank trigger user rank change
|
||||
// session is need provider, it means this action must be success or failure
|
||||
// if outer action is failed then this action is need rollback
|
||||
|
@ -38,10 +88,7 @@ func (ur *UserRankRepo) TriggerUserRank(ctx context.Context,
|
|||
session *xorm.Session, userID string, deltaRank int, activityType int,
|
||||
) (isReachStandard bool, err error) {
|
||||
// IMPORTANT: If user center enabled the rank agent, then we should not change user rank.
|
||||
if plugin.RankAgentEnabled() {
|
||||
return false, nil
|
||||
}
|
||||
if deltaRank == 0 {
|
||||
if plugin.RankAgentEnabled() || deltaRank == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,7 @@ func (ur *userRepo) GetByUsername(ctx context.Context, username string) (userInf
|
|||
|
||||
func (ur *userRepo) GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error) {
|
||||
list := make([]*entity.User, 0)
|
||||
err := ur.data.DB.Where("status =?", entity.UserStatusAvailable).In("username", usernames).Find(&list)
|
||||
err := ur.data.DB.Context(ctx).Where("status =?", entity.UserStatusAvailable).In("username", usernames).Find(&list)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
return list, err
|
||||
|
|
|
@ -6,20 +6,44 @@ type VoteReq struct {
|
|||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type VoteDTO struct {
|
||||
// object TagID
|
||||
ObjectID string
|
||||
// is cancel
|
||||
IsCancel bool
|
||||
// user TagID
|
||||
UserID string
|
||||
type VoteResp struct {
|
||||
UpVotes int64 `json:"up_votes"`
|
||||
DownVotes int64 `json:"down_votes"`
|
||||
Votes int64 `json:"votes"`
|
||||
VoteStatus string `json:"vote_status"`
|
||||
}
|
||||
|
||||
type VoteResp struct {
|
||||
UpVotes int `json:"up_votes"`
|
||||
DownVotes int `json:"down_votes"`
|
||||
Votes int `json:"votes"`
|
||||
VoteStatus string `json:"vote_status"`
|
||||
// VoteOperationInfo vote operation info
|
||||
type VoteOperationInfo struct {
|
||||
// operation object id
|
||||
ObjectID string
|
||||
// question answer comment
|
||||
ObjectType string
|
||||
// object owner user id
|
||||
ObjectCreatorUserID string
|
||||
// operation user id
|
||||
OperatingUserID string
|
||||
// vote up
|
||||
VoteUp bool
|
||||
// vote down
|
||||
VoteDown bool
|
||||
// vote activity info
|
||||
Activities []*VoteActivity
|
||||
}
|
||||
|
||||
// VoteActivity vote activity
|
||||
type VoteActivity struct {
|
||||
ActivityType int
|
||||
ActivityUserID string
|
||||
TriggerUserID string
|
||||
Rank int
|
||||
}
|
||||
|
||||
func (v *VoteActivity) HasRank() int {
|
||||
if v.Rank != 0 {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type GetVoteWithPageReq struct {
|
||||
|
@ -31,22 +55,6 @@ type GetVoteWithPageReq struct {
|
|||
UserID string `json:"-"`
|
||||
}
|
||||
|
||||
type VoteQuestion struct {
|
||||
// object ID
|
||||
ID string `json:"id"`
|
||||
// title
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
type VoteAnswer struct {
|
||||
// object ID
|
||||
ID string `json:"id"`
|
||||
// question ID
|
||||
QuestionID string `json:"question_id"`
|
||||
// title
|
||||
Title string `json:"title"`
|
||||
}
|
||||
|
||||
type GetVoteWithPageResp struct {
|
||||
// create time
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
|
|
|
@ -2,9 +2,6 @@ package activity
|
|||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
// AnswerActivityRepo answer activity
|
||||
|
@ -13,26 +10,18 @@ type AnswerActivityRepo interface {
|
|||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error)
|
||||
CancelAcceptAnswer(ctx context.Context,
|
||||
answerObjID, questionObjID, questionUserID, answerUserID string) (err error)
|
||||
DeleteAnswer(ctx context.Context, answerID string) (err error)
|
||||
}
|
||||
|
||||
// QuestionActivityRepo answer activity
|
||||
type QuestionActivityRepo interface {
|
||||
DeleteQuestion(ctx context.Context, questionID string) (err error)
|
||||
}
|
||||
|
||||
// AnswerActivityService user service
|
||||
type AnswerActivityService struct {
|
||||
answerActivityRepo AnswerActivityRepo
|
||||
questionActivityRepo QuestionActivityRepo
|
||||
answerActivityRepo AnswerActivityRepo
|
||||
}
|
||||
|
||||
// NewAnswerActivityService new comment service
|
||||
func NewAnswerActivityService(
|
||||
answerActivityRepo AnswerActivityRepo, questionActivityRepo QuestionActivityRepo) *AnswerActivityService {
|
||||
answerActivityRepo AnswerActivityRepo) *AnswerActivityService {
|
||||
return &AnswerActivityService{
|
||||
answerActivityRepo: answerActivityRepo,
|
||||
questionActivityRepo: questionActivityRepo,
|
||||
answerActivityRepo: answerActivityRepo,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,31 +36,3 @@ func (as *AnswerActivityService) CancelAcceptAnswer(ctx context.Context,
|
|||
answerObjID, questionObjID, questionUserID, answerUserID string) (err error) {
|
||||
return as.answerActivityRepo.CancelAcceptAnswer(ctx, answerObjID, questionObjID, questionUserID, answerUserID)
|
||||
}
|
||||
|
||||
// DeleteAnswer delete answer change activity
|
||||
func (as *AnswerActivityService) DeleteAnswer(ctx context.Context, answerID string, createdAt time.Time,
|
||||
voteCount int) (err error) {
|
||||
if voteCount >= 3 {
|
||||
log.Infof("There is no need to roll back the reputation by answering likes above the target value. %s %d", answerID, voteCount)
|
||||
return nil
|
||||
}
|
||||
if createdAt.Before(time.Now().AddDate(0, 0, -60)) {
|
||||
log.Infof("There is no need to roll back the reputation by answer's existence time meets the target. %s %s", answerID, createdAt.String())
|
||||
return nil
|
||||
}
|
||||
return as.answerActivityRepo.DeleteAnswer(ctx, answerID)
|
||||
}
|
||||
|
||||
// DeleteQuestion delete question change activity
|
||||
func (as *AnswerActivityService) DeleteQuestion(ctx context.Context, questionID string, createdAt time.Time,
|
||||
voteCount int) (err error) {
|
||||
if voteCount >= 3 {
|
||||
log.Infof("There is no need to roll back the reputation by answering likes above the target value. %s %d", questionID, voteCount)
|
||||
return nil
|
||||
}
|
||||
if createdAt.Before(time.Now().AddDate(0, 0, -60)) {
|
||||
log.Infof("There is no need to roll back the reputation by answer's existence time meets the target. %s %s", questionID, createdAt.String())
|
||||
return nil
|
||||
}
|
||||
return as.questionActivityRepo.DeleteQuestion(ctx, questionID)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
|
||||
type ActivityRepo interface {
|
||||
GetActivityTypeByObjID(ctx context.Context, objectId string, action string) (activityType, rank int, hasRank int, err error)
|
||||
GetActivityTypeByObjKey(ctx context.Context, objectKey, action string) (activityType int, err error)
|
||||
GetActivityTypeByObjectType(ctx context.Context, objectKey, action string) (activityType int, err error)
|
||||
GetActivity(ctx context.Context, session *xorm.Session, objectID, userID string, activityType int) (
|
||||
existsActivity *entity.Activity, exist bool, err error)
|
||||
GetUserIDObjectIDActivitySum(ctx context.Context, userID, objectID string) (int, error)
|
||||
|
|
|
@ -29,6 +29,10 @@ const (
|
|||
)
|
||||
|
||||
type UserRankRepo interface {
|
||||
GetMaxDailyRank(ctx context.Context) (maxDailyRank int, err error)
|
||||
CheckReachLimit(ctx context.Context, session *xorm.Session, userID string, maxDailyRank int) (reach bool, err error)
|
||||
ChangeUserRank(ctx context.Context, session *xorm.Session,
|
||||
userID string, userCurrentScore, deltaRank int) (err error)
|
||||
TriggerUserRank(ctx context.Context, session *xorm.Session, userId string, rank int, activityType int) (isReachStandard bool, err error)
|
||||
UserRankPage(ctx context.Context, userId string, page, pageSize int) (rankPage []*entity.Activity, total int64, err error)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package service
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"strings"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
|
@ -13,42 +15,37 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/config"
|
||||
"github.com/answerdev/answer/internal/service/object_info"
|
||||
"github.com/answerdev/answer/pkg/htmltext"
|
||||
"github.com/answerdev/answer/pkg/obj"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
||||
questioncommon "github.com/answerdev/answer/internal/service/question_common"
|
||||
"github.com/answerdev/answer/internal/service/unique"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// VoteRepo activity repository
|
||||
type VoteRepo interface {
|
||||
VoteUp(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
||||
VoteDown(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
||||
VoteUpCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
||||
VoteDownCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
||||
GetVoteResultByObjectId(ctx context.Context, objectID string) (resp *schema.VoteResp, err error)
|
||||
Vote(ctx context.Context, op *schema.VoteOperationInfo) (err error)
|
||||
CancelVote(ctx context.Context, op *schema.VoteOperationInfo) (err error)
|
||||
GetAndSaveVoteResult(ctx context.Context, objectID, objectType string) (up, down int64, err error)
|
||||
ListUserVotes(ctx context.Context, userID string, page int, pageSize int, activityTypes []int) (
|
||||
voteList []entity.Activity, total int64, err error)
|
||||
voteList []*entity.Activity, total int64, err error)
|
||||
}
|
||||
|
||||
// VoteService user service
|
||||
type VoteService struct {
|
||||
voteRepo VoteRepo
|
||||
UniqueIDRepo unique.UniqueIDRepo
|
||||
configService *config.ConfigService
|
||||
questionRepo questioncommon.QuestionRepo
|
||||
answerRepo answercommon.AnswerRepo
|
||||
commentCommonRepo comment_common.CommentCommonRepo
|
||||
objectService *object_info.ObjService
|
||||
activityRepo activity_common.ActivityRepo
|
||||
}
|
||||
|
||||
func NewVoteService(
|
||||
VoteRepo VoteRepo,
|
||||
uniqueIDRepo unique.UniqueIDRepo,
|
||||
voteRepo VoteRepo,
|
||||
configService *config.ConfigService,
|
||||
questionRepo questioncommon.QuestionRepo,
|
||||
answerRepo answercommon.AnswerRepo,
|
||||
|
@ -56,8 +53,7 @@ func NewVoteService(
|
|||
objectService *object_info.ObjService,
|
||||
) *VoteService {
|
||||
return &VoteService{
|
||||
voteRepo: VoteRepo,
|
||||
UniqueIDRepo: uniqueIDRepo,
|
||||
voteRepo: voteRepo,
|
||||
configService: configService,
|
||||
questionRepo: questionRepo,
|
||||
answerRepo: answerRepo,
|
||||
|
@ -67,90 +63,83 @@ func NewVoteService(
|
|||
}
|
||||
|
||||
// VoteUp vote up
|
||||
func (vs *VoteService) VoteUp(ctx context.Context, dto *schema.VoteDTO) (voteResp *schema.VoteResp, err error) {
|
||||
voteResp = &schema.VoteResp{}
|
||||
|
||||
var objectUserID string
|
||||
|
||||
objectUserID, err = vs.GetObjectUserID(ctx, dto.ObjectID)
|
||||
func (vs *VoteService) VoteUp(ctx context.Context, req *schema.VoteReq) (resp *schema.VoteResp, err error) {
|
||||
objectInfo, err := vs.objectService.GetInfo(ctx, req.ObjectID)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
// make object id must be decoded
|
||||
objectInfo.ObjectID = req.ObjectID
|
||||
|
||||
// check user is voting self or not
|
||||
if objectUserID == dto.UserID {
|
||||
err = errors.BadRequest(reason.DisallowVoteYourSelf)
|
||||
return
|
||||
if objectInfo.ObjectCreatorUserID == req.UserID {
|
||||
return nil, errors.BadRequest(reason.DisallowVoteYourSelf)
|
||||
}
|
||||
|
||||
if dto.IsCancel {
|
||||
return vs.voteRepo.VoteUpCancel(ctx, dto.ObjectID, dto.UserID, objectUserID)
|
||||
voteUpOperationInfo := vs.createVoteOperationInfo(ctx, req.UserID, true, objectInfo)
|
||||
|
||||
// vote operation
|
||||
if req.IsCancel {
|
||||
err = vs.voteRepo.CancelVote(ctx, voteUpOperationInfo)
|
||||
} else {
|
||||
return vs.voteRepo.VoteUp(ctx, dto.ObjectID, dto.UserID, objectUserID)
|
||||
// cancel vote down if exist
|
||||
voteOperationInfo := vs.createVoteOperationInfo(ctx, req.UserID, false, objectInfo)
|
||||
err = vs.voteRepo.CancelVote(ctx, voteOperationInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = vs.voteRepo.Vote(ctx, voteUpOperationInfo)
|
||||
}
|
||||
|
||||
resp = &schema.VoteResp{}
|
||||
resp.UpVotes, resp.DownVotes, err = vs.voteRepo.GetAndSaveVoteResult(ctx, req.ObjectID, objectInfo.ObjectType)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
resp.Votes = resp.UpVotes - resp.DownVotes
|
||||
if !req.IsCancel {
|
||||
resp.VoteStatus = constant.ActVoteUp
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// VoteDown vote down
|
||||
func (vs *VoteService) VoteDown(ctx context.Context, dto *schema.VoteDTO) (voteResp *schema.VoteResp, err error) {
|
||||
voteResp = &schema.VoteResp{}
|
||||
|
||||
var objectUserID string
|
||||
|
||||
objectUserID, err = vs.GetObjectUserID(ctx, dto.ObjectID)
|
||||
func (vs *VoteService) VoteDown(ctx context.Context, req *schema.VoteReq) (resp *schema.VoteResp, err error) {
|
||||
objectInfo, err := vs.objectService.GetInfo(ctx, req.ObjectID)
|
||||
if err != nil {
|
||||
return
|
||||
return nil, err
|
||||
}
|
||||
// make object id must be decoded
|
||||
objectInfo.ObjectID = req.ObjectID
|
||||
|
||||
// check user is voting self or not
|
||||
if objectUserID == dto.UserID {
|
||||
err = errors.BadRequest(reason.DisallowVoteYourSelf)
|
||||
return
|
||||
if objectInfo.ObjectCreatorUserID == req.UserID {
|
||||
return nil, errors.BadRequest(reason.DisallowVoteYourSelf)
|
||||
}
|
||||
|
||||
if dto.IsCancel {
|
||||
return vs.voteRepo.VoteDownCancel(ctx, dto.ObjectID, dto.UserID, objectUserID)
|
||||
// vote operation
|
||||
voteDownOperationInfo := vs.createVoteOperationInfo(ctx, req.UserID, false, objectInfo)
|
||||
if req.IsCancel {
|
||||
err = vs.voteRepo.CancelVote(ctx, voteDownOperationInfo)
|
||||
} else {
|
||||
return vs.voteRepo.VoteDown(ctx, dto.ObjectID, dto.UserID, objectUserID)
|
||||
// cancel vote up if exist
|
||||
err = vs.voteRepo.CancelVote(ctx, vs.createVoteOperationInfo(ctx, req.UserID, true, objectInfo))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = vs.voteRepo.Vote(ctx, voteDownOperationInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *VoteService) GetObjectUserID(ctx context.Context, objectID string) (userID string, err error) {
|
||||
var objectKey string
|
||||
objectKey, err = obj.GetObjectTypeStrByObjectID(objectID)
|
||||
|
||||
resp = &schema.VoteResp{}
|
||||
resp.UpVotes, resp.DownVotes, err = vs.voteRepo.GetAndSaveVoteResult(ctx, req.ObjectID, objectInfo.ObjectType)
|
||||
if err != nil {
|
||||
err = nil
|
||||
return
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
switch objectKey {
|
||||
case "question":
|
||||
object, has, e := vs.questionRepo.GetQuestion(ctx, objectID)
|
||||
if e != nil || !has {
|
||||
err = errors.BadRequest(reason.QuestionNotFound).WithError(e).WithStack()
|
||||
return
|
||||
}
|
||||
userID = object.UserID
|
||||
case "answer":
|
||||
object, has, e := vs.answerRepo.GetAnswer(ctx, objectID)
|
||||
if e != nil || !has {
|
||||
err = errors.BadRequest(reason.AnswerNotFound).WithError(e).WithStack()
|
||||
return
|
||||
}
|
||||
userID = object.UserID
|
||||
case "comment":
|
||||
object, has, e := vs.commentCommonRepo.GetComment(ctx, objectID)
|
||||
if e != nil || !has {
|
||||
err = errors.BadRequest(reason.CommentNotFound).WithError(e).WithStack()
|
||||
return
|
||||
}
|
||||
userID = object.UserID
|
||||
default:
|
||||
err = errors.BadRequest(reason.DisallowVote).WithError(err).WithStack()
|
||||
return
|
||||
resp.Votes = resp.UpVotes - resp.DownVotes
|
||||
if !req.IsCancel {
|
||||
resp.VoteStatus = constant.ActVoteDown
|
||||
}
|
||||
|
||||
return
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ListUserVotes list user's votes
|
||||
|
@ -207,3 +196,61 @@ func (vs *VoteService) ListUserVotes(ctx context.Context, req schema.GetVoteWith
|
|||
}
|
||||
return pager.NewPageModel(total, votes), err
|
||||
}
|
||||
|
||||
func (vs *VoteService) createVoteOperationInfo(ctx context.Context,
|
||||
userID string, voteUp bool, objectInfo *schema.SimpleObjectInfo) *schema.VoteOperationInfo {
|
||||
// warp vote operation
|
||||
voteOperationInfo := &schema.VoteOperationInfo{
|
||||
ObjectID: objectInfo.ObjectID,
|
||||
ObjectType: objectInfo.ObjectType,
|
||||
ObjectCreatorUserID: objectInfo.ObjectCreatorUserID,
|
||||
OperatingUserID: userID,
|
||||
VoteUp: voteUp,
|
||||
VoteDown: !voteUp,
|
||||
}
|
||||
voteOperationInfo.Activities = vs.getActivities(ctx, voteOperationInfo)
|
||||
return voteOperationInfo
|
||||
}
|
||||
|
||||
func (vs *VoteService) getActivities(ctx context.Context, op *schema.VoteOperationInfo) (
|
||||
activities []*schema.VoteActivity) {
|
||||
activities = make([]*schema.VoteActivity, 0)
|
||||
|
||||
var actions []string
|
||||
switch op.ObjectType {
|
||||
case constant.QuestionObjectType:
|
||||
if op.VoteUp {
|
||||
actions = []string{activity_type.QuestionVoteUp, activity_type.QuestionVotedUp}
|
||||
} else {
|
||||
actions = []string{activity_type.QuestionVoteDown, activity_type.QuestionVotedDown}
|
||||
}
|
||||
case constant.AnswerObjectType:
|
||||
if op.VoteUp {
|
||||
actions = []string{activity_type.AnswerVoteUp, activity_type.AnswerVotedUp}
|
||||
} else {
|
||||
actions = []string{activity_type.AnswerVoteDown, activity_type.AnswerVotedDown}
|
||||
}
|
||||
case constant.CommentObjectType:
|
||||
actions = []string{activity_type.CommentVoteUp}
|
||||
}
|
||||
|
||||
for _, action := range actions {
|
||||
t := &schema.VoteActivity{}
|
||||
cfg, err := vs.configService.GetConfigByKey(ctx, action)
|
||||
if err != nil {
|
||||
log.Warnf("get config by key error: %v", err)
|
||||
continue
|
||||
}
|
||||
t.ActivityType, t.Rank = cfg.ID, cfg.GetIntValue()
|
||||
|
||||
if strings.Contains(action, "voted") {
|
||||
t.ActivityUserID = op.ObjectCreatorUserID
|
||||
t.TriggerUserID = op.OperatingUserID
|
||||
} else {
|
||||
t.ActivityUserID = op.OperatingUserID
|
||||
t.TriggerUserID = "0"
|
||||
}
|
||||
activities = append(activities, t)
|
||||
}
|
||||
return activities
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue