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)
|
reportRepo := report.NewReportRepo(dataData, uniqueIDRepo)
|
||||||
reportService := report2.NewReportService(reportRepo, objService)
|
reportService := report2.NewReportService(reportRepo, objService)
|
||||||
reportController := controller.NewReportController(reportService, rankService)
|
reportController := controller.NewReportController(reportService, rankService)
|
||||||
serviceVoteRepo := activity.NewVoteRepo(dataData, uniqueIDRepo, configService, activityRepo, userRankRepo, voteRepo, notificationQueueService)
|
serviceVoteRepo := activity.NewVoteRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
|
||||||
voteService := service.NewVoteService(serviceVoteRepo, uniqueIDRepo, configService, questionRepo, answerRepo, commentCommonRepo, objService)
|
voteService := service.NewVoteService(serviceVoteRepo, configService, questionRepo, answerRepo, commentCommonRepo, objService)
|
||||||
voteController := controller.NewVoteController(voteService, rankService)
|
voteController := controller.NewVoteController(voteService, rankService)
|
||||||
followRepo := activity_common.NewFollowRepo(dataData, uniqueIDRepo, activityRepo)
|
followRepo := activity_common.NewFollowRepo(dataData, uniqueIDRepo, activityRepo)
|
||||||
tagService := tag2.NewTagService(tagRepo, tagCommonService, revisionService, followRepo, siteInfoCommonService, activityQueueService)
|
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)
|
collectionService := service.NewCollectionService(collectionRepo, collectionGroupRepo, questionCommon)
|
||||||
collectionController := controller.NewCollectionController(collectionService)
|
collectionController := controller.NewCollectionController(collectionService)
|
||||||
answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
|
answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
|
||||||
questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo)
|
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo)
|
||||||
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo)
|
|
||||||
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, userRepo, revisionService, metaService, collectionCommon, answerActivityService, emailService, notificationQueueService, activityQueueService, siteInfoCommonService)
|
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)
|
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService, notificationQueueService, activityQueueService)
|
||||||
questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService)
|
questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService)
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/answerdev/answer/internal/service/rank"
|
"github.com/answerdev/answer/internal/service/rank"
|
||||||
"github.com/answerdev/answer/pkg/uid"
|
"github.com/answerdev/answer/pkg/uid"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jinzhu/copier"
|
|
||||||
"github.com/segmentfault/pacman/errors"
|
"github.com/segmentfault/pacman/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,9 +53,7 @@ func (vc *VoteController) VoteUp(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dto := &schema.VoteDTO{}
|
resp, err := vc.VoteService.VoteUp(ctx, req)
|
||||||
_ = copier.Copy(dto, req)
|
|
||||||
resp, err := vc.VoteService.VoteUp(ctx, dto)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,9 +90,7 @@ func (vc *VoteController) VoteDown(ctx *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dto := &schema.VoteDTO{}
|
resp, err := vc.VoteService.VoteDown(ctx, req)
|
||||||
_ = copier.Copy(dto, req)
|
|
||||||
resp, err := vc.VoteService.VoteDown(ctx, dto)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
handler.HandleResponse(ctx, err, schema.ErrTypeToast)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
"github.com/answerdev/answer/internal/service/rank"
|
"github.com/answerdev/answer/internal/service/rank"
|
||||||
"github.com/answerdev/answer/pkg/converter"
|
"github.com/answerdev/answer/pkg/converter"
|
||||||
"github.com/segmentfault/pacman/errors"
|
"github.com/segmentfault/pacman/errors"
|
||||||
"github.com/segmentfault/pacman/log"
|
|
||||||
"xorm.io/xorm"
|
"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
|
// AcceptAnswer accept other answer
|
||||||
func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
|
func (ar *AnswerActivityRepo) AcceptAnswer(ctx context.Context,
|
||||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool,
|
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool,
|
||||||
|
@ -306,50 +232,3 @@ func (ar *AnswerActivityRepo) CancelAcceptAnswer(ctx context.Context,
|
||||||
}
|
}
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,7 @@ func (ar *FollowRepo) FollowCancel(ctx context.Context, objectID, userID string)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package activity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"xorm.io/builder"
|
||||||
|
|
||||||
"github.com/answerdev/answer/internal/base/data"
|
"github.com/answerdev/answer/internal/base/data"
|
||||||
"github.com/answerdev/answer/internal/base/reason"
|
"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) {
|
func (ar *UserActiveActivityRepo) UserActive(ctx context.Context, userID string) (err error) {
|
||||||
cfg, err := ar.configService.GetConfigByKey(ctx, UserActivated)
|
cfg, err := ar.configService.GetConfigByKey(ctx, UserActivated)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
activityType := cfg.ID
|
|
||||||
deltaRank := cfg.GetIntValue()
|
|
||||||
addActivity := &entity.Activity{
|
addActivity := &entity.Activity{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
ObjectID: "0",
|
ObjectID: "0",
|
||||||
OriginalObjectID: "0",
|
OriginalObjectID: "0",
|
||||||
ActivityType: activityType,
|
ActivityType: cfg.ID,
|
||||||
Rank: deltaRank,
|
Rank: cfg.GetIntValue(),
|
||||||
HasRank: 1,
|
HasRank: 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ar.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
_, err = ar.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||||
session = session.Context(ctx)
|
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 {
|
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
|
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 {
|
if err != nil {
|
||||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = session.Insert(addActivity)
|
_, err = session.Insert(addActivity)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
return nil, err
|
||||||
}
|
}
|
||||||
return nil, nil
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"fmt"
|
||||||
|
"github.com/segmentfault/pacman/log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/answerdev/answer/internal/base/constant"
|
"github.com/answerdev/answer/internal/base/constant"
|
||||||
|
@ -10,20 +11,17 @@ import (
|
||||||
"github.com/answerdev/answer/pkg/converter"
|
"github.com/answerdev/answer/pkg/converter"
|
||||||
|
|
||||||
"github.com/answerdev/answer/internal/base/pager"
|
"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/internal/service/rank"
|
||||||
"github.com/answerdev/answer/pkg/obj"
|
"github.com/answerdev/answer/pkg/obj"
|
||||||
|
|
||||||
"xorm.io/builder"
|
"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/data"
|
||||||
"github.com/answerdev/answer/internal/base/reason"
|
"github.com/answerdev/answer/internal/base/reason"
|
||||||
"github.com/answerdev/answer/internal/entity"
|
"github.com/answerdev/answer/internal/entity"
|
||||||
"github.com/answerdev/answer/internal/schema"
|
"github.com/answerdev/answer/internal/schema"
|
||||||
"github.com/answerdev/answer/internal/service"
|
"github.com/answerdev/answer/internal/service"
|
||||||
|
"github.com/answerdev/answer/internal/service/activity_common"
|
||||||
"github.com/segmentfault/pacman/errors"
|
"github.com/segmentfault/pacman/errors"
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
@ -31,365 +29,147 @@ import (
|
||||||
// VoteRepo activity repository
|
// VoteRepo activity repository
|
||||||
type VoteRepo struct {
|
type VoteRepo struct {
|
||||||
data *data.Data
|
data *data.Data
|
||||||
uniqueIDRepo unique.UniqueIDRepo
|
|
||||||
configService *config.ConfigService
|
|
||||||
activityRepo activity_common.ActivityRepo
|
activityRepo activity_common.ActivityRepo
|
||||||
userRankRepo rank.UserRankRepo
|
userRankRepo rank.UserRankRepo
|
||||||
voteCommon activity_common.VoteRepo
|
|
||||||
notificationQueueService notice_queue.NotificationQueueService
|
notificationQueueService notice_queue.NotificationQueueService
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewVoteRepo new repository
|
// NewVoteRepo new repository
|
||||||
func NewVoteRepo(
|
func NewVoteRepo(
|
||||||
data *data.Data,
|
data *data.Data,
|
||||||
uniqueIDRepo unique.UniqueIDRepo,
|
|
||||||
configService *config.ConfigService,
|
|
||||||
activityRepo activity_common.ActivityRepo,
|
activityRepo activity_common.ActivityRepo,
|
||||||
userRankRepo rank.UserRankRepo,
|
userRankRepo rank.UserRankRepo,
|
||||||
voteCommon activity_common.VoteRepo,
|
|
||||||
notificationQueueService notice_queue.NotificationQueueService,
|
notificationQueueService notice_queue.NotificationQueueService,
|
||||||
) service.VoteRepo {
|
) service.VoteRepo {
|
||||||
return &VoteRepo{
|
return &VoteRepo{
|
||||||
data: data,
|
data: data,
|
||||||
uniqueIDRepo: uniqueIDRepo,
|
|
||||||
configService: configService,
|
|
||||||
activityRepo: activityRepo,
|
activityRepo: activityRepo,
|
||||||
userRankRepo: userRankRepo,
|
userRankRepo: userRankRepo,
|
||||||
voteCommon: voteCommon,
|
|
||||||
notificationQueueService: notificationQueueService,
|
notificationQueueService: notificationQueueService,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var LimitUpActions = map[string][]string{
|
func (vr *VoteRepo) Vote(ctx context.Context, op *schema.VoteOperationInfo) (err error) {
|
||||||
"question": {"vote_up", "voted_up"},
|
noNeedToVote, err := vr.votePreCheck(ctx, op)
|
||||||
"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
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
|
}
|
||||||
|
if noNeedToVote {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err = vr.GetVoteResultByObjectId(ctx, objectID)
|
sendInboxNotification := false
|
||||||
resp.VoteStatus = vr.voteCommon.GetVoteStatus(ctx, objectID, userID)
|
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 {
|
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||||
vr.sendNotification(ctx, activityUserID, objectUserID, objectID)
|
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 {
|
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) {
|
func (vr *VoteRepo) CancelVote(ctx context.Context, op *schema.VoteOperationInfo) (err error) {
|
||||||
resp = &schema.VoteResp{}
|
// Pre-Check
|
||||||
notificationUserIDs := make([]string, 0)
|
// 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) {
|
_, err = vr.data.DB.Transaction(func(session *xorm.Session) (result any, err error) {
|
||||||
session = session.Context(ctx)
|
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)
|
userInfoMapping, err := vr.acquireUserInfo(session, userIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
resp, err = vr.GetVoteResultByObjectId(ctx, objectID)
|
|
||||||
resp.VoteStatus = vr.voteCommon.GetVoteStatus(ctx, objectID, userID)
|
|
||||||
|
|
||||||
for _, activityUserID := range notificationUserIDs {
|
for _, activity := range activities {
|
||||||
vr.sendNotification(ctx, activityUserID, objectUserID, objectID)
|
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
|
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,
|
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)
|
session := vr.data.DB.Context(ctx)
|
||||||
cond := builder.
|
cond := builder.
|
||||||
And(
|
And(
|
||||||
|
@ -407,37 +187,234 @@ func (vr *VoteRepo) ListUserVotes(ctx context.Context, userID string,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// updateVotes
|
func (vr *VoteRepo) votePreCheck(ctx context.Context, op *schema.VoteOperationInfo) (noNeedToVote bool, err error) {
|
||||||
// if votes < 0 Decr object vote_count,otherwise Incr object vote_count
|
activities, err := vr.getExistActivity(ctx, op)
|
||||||
func (vr *VoteRepo) updateVotes(ctx context.Context, session *xorm.Session, objectID string, votes int) (err error) {
|
if err != nil {
|
||||||
var (
|
return false, err
|
||||||
objectType string
|
}
|
||||||
e error
|
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 {
|
switch objectType {
|
||||||
case "question":
|
case constant.QuestionObjectType:
|
||||||
_, err = session.Where("id = ?", objectID).Incr("vote_count", votes).Update(&entity.Question{})
|
_, err = session.ID(objectID).Cols("vote_count").Update(&entity.Question{VoteCount: voteCount})
|
||||||
case "answer":
|
case constant.AnswerObjectType:
|
||||||
_, err = session.Where("id = ?", objectID).Incr("vote_count", votes).Update(&entity.Answer{})
|
_, err = session.ID(objectID).Cols("vote_count").Update(&entity.Answer{VoteCount: voteCount})
|
||||||
case "comment":
|
case constant.CommentObjectType:
|
||||||
_, err = session.Where("id = ?", objectID).Incr("vote_count", votes).Update(&entity.Comment{})
|
_, err = session.ID(objectID).Cols("vote_count").Update(&entity.Comment{VoteCount: voteCount})
|
||||||
default:
|
|
||||||
e = errors.BadRequest(reason.DisallowVote)
|
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
if e != nil {
|
log.Error(err)
|
||||||
err = e
|
|
||||||
} else if err != nil {
|
|
||||||
err = errors.BadRequest(reason.DatabaseError).WithError(err).WithStack()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendNotification send rank triggered notification
|
func (vr *VoteRepo) sendAchievementNotification(ctx context.Context, activityUserID, objectUserID, objectID string) {
|
||||||
func (vr *VoteRepo) sendNotification(ctx context.Context, activityUserID, objectUserID, objectID string) {
|
|
||||||
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -41,12 +41,12 @@ func NewActivityRepo(
|
||||||
|
|
||||||
func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectID string, action string) (
|
func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectID string, action string) (
|
||||||
activityType, rank, hasRank int, err error) {
|
activityType, rank, hasRank int, err error) {
|
||||||
objectKey, err := obj.GetObjectTypeStrByObjectID(objectID)
|
objectType, err := obj.GetObjectTypeStrByObjectID(objectID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
confKey := fmt.Sprintf("%s.%s", objectKey, action)
|
confKey := fmt.Sprintf("%s.%s", objectType, action)
|
||||||
cfg, err := ar.configService.GetConfigByKey(ctx, confKey)
|
cfg, err := ar.configService.GetConfigByKey(ctx, confKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -59,8 +59,8 @@ func (ar *ActivityRepo) GetActivityTypeByObjID(ctx context.Context, objectID str
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ar *ActivityRepo) GetActivityTypeByObjKey(ctx context.Context, objectKey, action string) (activityType int, err error) {
|
func (ar *ActivityRepo) GetActivityTypeByObjectType(ctx context.Context, objectType, action string) (activityType int, err error) {
|
||||||
configKey := fmt.Sprintf("%s.%s", objectKey, action)
|
configKey := fmt.Sprintf("%s.%s", objectType, action)
|
||||||
cfg, err := ar.configService.GetConfigByKey(ctx, configKey)
|
cfg, err := ar.configService.GetConfigByKey(ctx, configKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectTypeStr, "follow")
|
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectTypeStr, "follow")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("can't get activity type by object key: %s", objectTypeStr)
|
log.Errorf("can't get activity type by object key: %s", objectTypeStr)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -96,7 +96,7 @@ func (ar *FollowRepo) GetFollowUserIDs(ctx context.Context, objectID string) (us
|
||||||
// GetFollowIDs get all follow id list
|
// GetFollowIDs get all follow id list
|
||||||
func (ar *FollowRepo) GetFollowIDs(ctx context.Context, userID, objectKey string) (followIDs []string, err error) {
|
func (ar *FollowRepo) GetFollowIDs(ctx context.Context, userID, objectKey string) (followIDs []string, err error) {
|
||||||
followIDs = make([]string, 0)
|
followIDs = make([]string, 0)
|
||||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectKey, "follow")
|
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectKey, "follow")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
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
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
activityType, err := ar.activityRepo.GetActivityTypeByObjKey(ctx, objectKey, "follow")
|
activityType, err := ar.activityRepo.GetActivityTypeByObjectType(ctx, objectKey, "follow")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,6 @@ var ProviderSetRepo = wire.NewSet(
|
||||||
activity.NewVoteRepo,
|
activity.NewVoteRepo,
|
||||||
activity.NewFollowRepo,
|
activity.NewFollowRepo,
|
||||||
activity.NewAnswerActivityRepo,
|
activity.NewAnswerActivityRepo,
|
||||||
activity.NewQuestionActivityRepo,
|
|
||||||
activity.NewUserActiveActivityRepo,
|
activity.NewUserActiveActivityRepo,
|
||||||
activity.NewActivityRepo,
|
activity.NewActivityRepo,
|
||||||
tag.NewTagRepo,
|
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
|
// TriggerUserRank trigger user rank change
|
||||||
// session is need provider, it means this action must be success or failure
|
// session is need provider, it means this action must be success or failure
|
||||||
// if outer action is failed then this action is need rollback
|
// 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,
|
session *xorm.Session, userID string, deltaRank int, activityType int,
|
||||||
) (isReachStandard bool, err error) {
|
) (isReachStandard bool, err error) {
|
||||||
// IMPORTANT: If user center enabled the rank agent, then we should not change user rank.
|
// IMPORTANT: If user center enabled the rank agent, then we should not change user rank.
|
||||||
if plugin.RankAgentEnabled() {
|
if plugin.RankAgentEnabled() || deltaRank == 0 {
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if deltaRank == 0 {
|
|
||||||
return false, nil
|
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) {
|
func (ur *userRepo) GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error) {
|
||||||
list := make([]*entity.User, 0)
|
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 {
|
if err != nil {
|
||||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||||
return list, err
|
return list, err
|
||||||
|
|
|
@ -6,20 +6,44 @@ type VoteReq struct {
|
||||||
UserID string `json:"-"`
|
UserID string `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoteDTO struct {
|
type VoteResp struct {
|
||||||
// object TagID
|
UpVotes int64 `json:"up_votes"`
|
||||||
ObjectID string
|
DownVotes int64 `json:"down_votes"`
|
||||||
// is cancel
|
Votes int64 `json:"votes"`
|
||||||
IsCancel bool
|
VoteStatus string `json:"vote_status"`
|
||||||
// user TagID
|
|
||||||
UserID string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type VoteResp struct {
|
// VoteOperationInfo vote operation info
|
||||||
UpVotes int `json:"up_votes"`
|
type VoteOperationInfo struct {
|
||||||
DownVotes int `json:"down_votes"`
|
// operation object id
|
||||||
Votes int `json:"votes"`
|
ObjectID string
|
||||||
VoteStatus string `json:"vote_status"`
|
// 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 {
|
type GetVoteWithPageReq struct {
|
||||||
|
@ -31,22 +55,6 @@ type GetVoteWithPageReq struct {
|
||||||
UserID string `json:"-"`
|
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 {
|
type GetVoteWithPageResp struct {
|
||||||
// create time
|
// create time
|
||||||
CreatedAt int64 `json:"created_at"`
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
|
|
@ -2,9 +2,6 @@ package activity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/segmentfault/pacman/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// AnswerActivityRepo answer activity
|
// AnswerActivityRepo answer activity
|
||||||
|
@ -13,26 +10,18 @@ type AnswerActivityRepo interface {
|
||||||
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error)
|
answerObjID, questionObjID, questionUserID, answerUserID string, isSelf bool) (err error)
|
||||||
CancelAcceptAnswer(ctx context.Context,
|
CancelAcceptAnswer(ctx context.Context,
|
||||||
answerObjID, questionObjID, questionUserID, answerUserID string) (err error)
|
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
|
// AnswerActivityService user service
|
||||||
type AnswerActivityService struct {
|
type AnswerActivityService struct {
|
||||||
answerActivityRepo AnswerActivityRepo
|
answerActivityRepo AnswerActivityRepo
|
||||||
questionActivityRepo QuestionActivityRepo
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAnswerActivityService new comment service
|
// NewAnswerActivityService new comment service
|
||||||
func NewAnswerActivityService(
|
func NewAnswerActivityService(
|
||||||
answerActivityRepo AnswerActivityRepo, questionActivityRepo QuestionActivityRepo) *AnswerActivityService {
|
answerActivityRepo AnswerActivityRepo) *AnswerActivityService {
|
||||||
return &AnswerActivityService{
|
return &AnswerActivityService{
|
||||||
answerActivityRepo: answerActivityRepo,
|
answerActivityRepo: answerActivityRepo,
|
||||||
questionActivityRepo: questionActivityRepo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,31 +36,3 @@ func (as *AnswerActivityService) CancelAcceptAnswer(ctx context.Context,
|
||||||
answerObjID, questionObjID, questionUserID, answerUserID string) (err error) {
|
answerObjID, questionObjID, questionUserID, answerUserID string) (err error) {
|
||||||
return as.answerActivityRepo.CancelAcceptAnswer(ctx, answerObjID, questionObjID, questionUserID, answerUserID)
|
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 {
|
type ActivityRepo interface {
|
||||||
GetActivityTypeByObjID(ctx context.Context, objectId string, action string) (activityType, rank int, hasRank int, err error)
|
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) (
|
GetActivity(ctx context.Context, session *xorm.Session, objectID, userID string, activityType int) (
|
||||||
existsActivity *entity.Activity, exist bool, err error)
|
existsActivity *entity.Activity, exist bool, err error)
|
||||||
GetUserIDObjectIDActivitySum(ctx context.Context, userID, objectID string) (int, error)
|
GetUserIDObjectIDActivitySum(ctx context.Context, userID, objectID string) (int, error)
|
||||||
|
|
|
@ -29,6 +29,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserRankRepo interface {
|
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)
|
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)
|
UserRankPage(ctx context.Context, userId string, page, pageSize int) (rankPage []*entity.Activity, total int64, err error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/answerdev/answer/internal/service/activity_common"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/answerdev/answer/internal/base/constant"
|
"github.com/answerdev/answer/internal/base/constant"
|
||||||
"github.com/answerdev/answer/internal/base/handler"
|
"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/config"
|
||||||
"github.com/answerdev/answer/internal/service/object_info"
|
"github.com/answerdev/answer/internal/service/object_info"
|
||||||
"github.com/answerdev/answer/pkg/htmltext"
|
"github.com/answerdev/answer/pkg/htmltext"
|
||||||
"github.com/answerdev/answer/pkg/obj"
|
|
||||||
"github.com/segmentfault/pacman/log"
|
"github.com/segmentfault/pacman/log"
|
||||||
|
|
||||||
"github.com/answerdev/answer/internal/base/reason"
|
"github.com/answerdev/answer/internal/base/reason"
|
||||||
"github.com/answerdev/answer/internal/schema"
|
"github.com/answerdev/answer/internal/schema"
|
||||||
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
||||||
questioncommon "github.com/answerdev/answer/internal/service/question_common"
|
questioncommon "github.com/answerdev/answer/internal/service/question_common"
|
||||||
"github.com/answerdev/answer/internal/service/unique"
|
|
||||||
"github.com/segmentfault/pacman/errors"
|
"github.com/segmentfault/pacman/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VoteRepo activity repository
|
// VoteRepo activity repository
|
||||||
type VoteRepo interface {
|
type VoteRepo interface {
|
||||||
VoteUp(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
Vote(ctx context.Context, op *schema.VoteOperationInfo) (err error)
|
||||||
VoteDown(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
CancelVote(ctx context.Context, op *schema.VoteOperationInfo) (err error)
|
||||||
VoteUpCancel(ctx context.Context, objectID string, userID, objectUserID string) (resp *schema.VoteResp, err error)
|
GetAndSaveVoteResult(ctx context.Context, objectID, objectType string) (up, down int64, 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)
|
|
||||||
ListUserVotes(ctx context.Context, userID string, page int, pageSize int, activityTypes []int) (
|
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
|
// VoteService user service
|
||||||
type VoteService struct {
|
type VoteService struct {
|
||||||
voteRepo VoteRepo
|
voteRepo VoteRepo
|
||||||
UniqueIDRepo unique.UniqueIDRepo
|
|
||||||
configService *config.ConfigService
|
configService *config.ConfigService
|
||||||
questionRepo questioncommon.QuestionRepo
|
questionRepo questioncommon.QuestionRepo
|
||||||
answerRepo answercommon.AnswerRepo
|
answerRepo answercommon.AnswerRepo
|
||||||
commentCommonRepo comment_common.CommentCommonRepo
|
commentCommonRepo comment_common.CommentCommonRepo
|
||||||
objectService *object_info.ObjService
|
objectService *object_info.ObjService
|
||||||
|
activityRepo activity_common.ActivityRepo
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVoteService(
|
func NewVoteService(
|
||||||
VoteRepo VoteRepo,
|
voteRepo VoteRepo,
|
||||||
uniqueIDRepo unique.UniqueIDRepo,
|
|
||||||
configService *config.ConfigService,
|
configService *config.ConfigService,
|
||||||
questionRepo questioncommon.QuestionRepo,
|
questionRepo questioncommon.QuestionRepo,
|
||||||
answerRepo answercommon.AnswerRepo,
|
answerRepo answercommon.AnswerRepo,
|
||||||
|
@ -56,8 +53,7 @@ func NewVoteService(
|
||||||
objectService *object_info.ObjService,
|
objectService *object_info.ObjService,
|
||||||
) *VoteService {
|
) *VoteService {
|
||||||
return &VoteService{
|
return &VoteService{
|
||||||
voteRepo: VoteRepo,
|
voteRepo: voteRepo,
|
||||||
UniqueIDRepo: uniqueIDRepo,
|
|
||||||
configService: configService,
|
configService: configService,
|
||||||
questionRepo: questionRepo,
|
questionRepo: questionRepo,
|
||||||
answerRepo: answerRepo,
|
answerRepo: answerRepo,
|
||||||
|
@ -67,90 +63,83 @@ func NewVoteService(
|
||||||
}
|
}
|
||||||
|
|
||||||
// VoteUp vote up
|
// VoteUp vote up
|
||||||
func (vs *VoteService) VoteUp(ctx context.Context, dto *schema.VoteDTO) (voteResp *schema.VoteResp, err error) {
|
func (vs *VoteService) VoteUp(ctx context.Context, req *schema.VoteReq) (resp *schema.VoteResp, err error) {
|
||||||
voteResp = &schema.VoteResp{}
|
objectInfo, err := vs.objectService.GetInfo(ctx, req.ObjectID)
|
||||||
|
|
||||||
var objectUserID string
|
|
||||||
|
|
||||||
objectUserID, err = vs.GetObjectUserID(ctx, dto.ObjectID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// make object id must be decoded
|
||||||
|
objectInfo.ObjectID = req.ObjectID
|
||||||
|
|
||||||
// check user is voting self or not
|
// check user is voting self or not
|
||||||
if objectUserID == dto.UserID {
|
if objectInfo.ObjectCreatorUserID == req.UserID {
|
||||||
err = errors.BadRequest(reason.DisallowVoteYourSelf)
|
return nil, errors.BadRequest(reason.DisallowVoteYourSelf)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if dto.IsCancel {
|
voteUpOperationInfo := vs.createVoteOperationInfo(ctx, req.UserID, true, objectInfo)
|
||||||
return vs.voteRepo.VoteUpCancel(ctx, dto.ObjectID, dto.UserID, objectUserID)
|
|
||||||
|
// vote operation
|
||||||
|
if req.IsCancel {
|
||||||
|
err = vs.voteRepo.CancelVote(ctx, voteUpOperationInfo)
|
||||||
} else {
|
} 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
|
// VoteDown vote down
|
||||||
func (vs *VoteService) VoteDown(ctx context.Context, dto *schema.VoteDTO) (voteResp *schema.VoteResp, err error) {
|
func (vs *VoteService) VoteDown(ctx context.Context, req *schema.VoteReq) (resp *schema.VoteResp, err error) {
|
||||||
voteResp = &schema.VoteResp{}
|
objectInfo, err := vs.objectService.GetInfo(ctx, req.ObjectID)
|
||||||
|
|
||||||
var objectUserID string
|
|
||||||
|
|
||||||
objectUserID, err = vs.GetObjectUserID(ctx, dto.ObjectID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// make object id must be decoded
|
||||||
|
objectInfo.ObjectID = req.ObjectID
|
||||||
|
|
||||||
// check user is voting self or not
|
// check user is voting self or not
|
||||||
if objectUserID == dto.UserID {
|
if objectInfo.ObjectCreatorUserID == req.UserID {
|
||||||
err = errors.BadRequest(reason.DisallowVoteYourSelf)
|
return nil, errors.BadRequest(reason.DisallowVoteYourSelf)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if dto.IsCancel {
|
// vote operation
|
||||||
return vs.voteRepo.VoteDownCancel(ctx, dto.ObjectID, dto.UserID, objectUserID)
|
voteDownOperationInfo := vs.createVoteOperationInfo(ctx, req.UserID, false, objectInfo)
|
||||||
|
if req.IsCancel {
|
||||||
|
err = vs.voteRepo.CancelVote(ctx, voteDownOperationInfo)
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
err = nil
|
log.Error(err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
resp.Votes = resp.UpVotes - resp.DownVotes
|
||||||
switch objectKey {
|
if !req.IsCancel {
|
||||||
case "question":
|
resp.VoteStatus = constant.ActVoteDown
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
return resp, nil
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListUserVotes list user's votes
|
// 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
|
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