feat(user): add user ranking api

This commit is contained in:
LinkinStar 2022-12-12 16:39:48 +08:00
parent 18ca572a38
commit 79158f54bd
9 changed files with 214 additions and 9 deletions

View File

@ -117,7 +117,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
roleService := role2.NewRoleService(roleRepo)
userRoleRelService := role2.NewUserRoleRelService(userRoleRelRepo, roleService)
userCommon := usercommon.NewUserCommon(userRepo)
userService := service.NewUserService(userRepo, userActiveActivityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService, userCommon)
userService := service.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, serviceConf, siteInfoCommonService, userRoleRelService, userCommon)
captchaRepo := captcha.NewCaptchaRepo(dataData)
captchaService := action.NewCaptchaService(captchaRepo)
uploaderService := uploader.NewUploaderService(serviceConf, siteInfoCommonService)

View File

@ -28,6 +28,16 @@ type ActivityRankSum struct {
Rank int `xorm:"not null default 0 INT(11) rank"`
}
type ActivityUserRankStat struct {
UserID string `xorm:"user_id"`
Rank int `xorm:"rank_amount"`
}
type ActivityUserVoteStat struct {
UserID string `xorm:"user_id"`
VoteCount int `xorm:"vote_count"`
}
// TableName activity table name
func (Activity) TableName() string {
return "activity"

View File

@ -7,6 +7,7 @@ import (
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/activity_common"
"github.com/answerdev/answer/internal/service/activity_type"
"github.com/answerdev/answer/pkg/obj"
"xorm.io/builder"
"xorm.io/xorm"
@ -110,6 +111,46 @@ func (ar *ActivityRepo) AddActivity(ctx context.Context, activity *entity.Activi
// GetUsersWhoHasGainedTheMostReputation get users who has gained the most reputation over a period of time
func (ar *ActivityRepo) GetUsersWhoHasGainedTheMostReputation(
ctx context.Context, startTime, endTime time.Time, limit int) (userIDs []string, err error) {
ctx context.Context, startTime, endTime time.Time, limit int) (rankStat []*entity.ActivityUserRankStat, err error) {
rankStat = make([]*entity.ActivityUserRankStat, 0)
session := ar.data.DB.Select("user_id, SUM(rank) AS rank_amount").Table("activity")
session.Where("has_rank = 1 AND cancelled = 0")
session.Where("created_at >= ?", startTime)
session.Where("created_at <= ?", endTime)
session.GroupBy("user_id")
session.Desc("rank_amount")
session.Limit(limit)
err = session.Find(&rankStat)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetUsersWhoHasVoteMost get users who has vote most
func (ar *ActivityRepo) GetUsersWhoHasVoteMost(
ctx context.Context, startTime, endTime time.Time, limit int) (voteStat []*entity.ActivityUserVoteStat, err error) {
voteStat = make([]*entity.ActivityUserVoteStat, 0)
actIDs := make([]int, 0)
for _, act := range activity_type.ActivityTypeList {
configType, err := ar.configRepo.GetConfigType(act)
if err == nil {
actIDs = append(actIDs, configType)
}
}
session := ar.data.DB.Select("user_id, COUNT(*) AS vote_count").Table("activity")
session.Where("cancelled = 0")
session.In("activity_type", actIDs)
session.Where("created_at >= ?", startTime)
session.Where("created_at <= ?", endTime)
session.GroupBy("user_id")
session.Desc("vote_count")
session.Limit(limit)
err = session.Find(&voteStat)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}

View File

@ -60,6 +60,17 @@ func (ur *userRoleRelRepo) GetUserRoleRelList(ctx context.Context, userIDs []str
return
}
// GetUserRoleRelListByRoleID get user role all by role id
func (ur *userRoleRelRepo) GetUserRoleRelListByRoleID(ctx context.Context, roleIDs []int) (
userRoleRelList []*entity.UserRoleRel, err error) {
userRoleRelList = make([]*entity.UserRoleRel, 0)
err = ur.data.DB.In("role_id", roleIDs).Find(&userRoleRelList)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetUserRoleRel get user role
func (ur *userRoleRelRepo) GetUserRoleRel(ctx context.Context, userID string) (
rolePowerRel *entity.UserRoleRel, exist bool, err error) {

View File

@ -106,16 +106,16 @@ func FormatAvatarInfo(avatarJson string) string {
if avatarJson == "" {
return ""
}
AvatarInfo := &AvatarInfo{}
err := json.Unmarshal([]byte(avatarJson), AvatarInfo)
avatarInfo := &AvatarInfo{}
err := json.Unmarshal([]byte(avatarJson), avatarInfo)
if err != nil {
return ""
}
switch AvatarInfo.Type {
switch avatarInfo.Type {
case "gravatar":
return AvatarInfo.Gravatar
return avatarInfo.Gravatar
case "custom":
return AvatarInfo.Custom
return avatarInfo.Custom
default:
return ""
}
@ -431,6 +431,8 @@ type UserRankingSimpleInfo struct {
Username string `json:"username"`
// rank
Rank int `json:"rank"`
// vote
VoteCount int `json:"vote_count"`
// display name
DisplayName string `json:"display_name"`
// avatar

View File

@ -2,6 +2,7 @@ package activity_common
import (
"context"
"time"
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/service/activity_queue"
@ -18,6 +19,10 @@ type ActivityRepo interface {
GetUserIDObjectIDActivitySum(ctx context.Context, userID, objectID string) (int, error)
GetActivityTypeByConfigKey(ctx context.Context, configKey string) (activityType int, err error)
AddActivity(ctx context.Context, activity *entity.Activity) (err error)
GetUsersWhoHasGainedTheMostReputation(
ctx context.Context, startTime, endTime time.Time, limit int) (rankStat []*entity.ActivityUserRankStat, err error)
GetUsersWhoHasVoteMost(
ctx context.Context, startTime, endTime time.Time, limit int) (voteStat []*entity.ActivityUserVoteStat, err error)
}
type ActivityCommon struct {

View File

@ -12,6 +12,14 @@ const (
)
var (
ActivityTypeList = []string{
QuestionVoteUp,
QuestionVoteDown,
AnswerVoteUp,
AnswerVoteDown,
CommentVoteUp,
CommentVoteDown,
}
activityTypeFlagMapping = map[string]string{
QuestionVoteUp: "upvote",
QuestionVoteDown: "downvote",

View File

@ -10,6 +10,8 @@ import (
type UserRoleRelRepo interface {
SaveUserRoleRel(ctx context.Context, userID string, roleID int) (err error)
GetUserRoleRelList(ctx context.Context, userIDs []string) (userRoleRelList []*entity.UserRoleRel, err error)
GetUserRoleRelListByRoleID(ctx context.Context, roleIDs []int) (
userRoleRelList []*entity.UserRoleRel, err error)
GetUserRoleRel(ctx context.Context, userID string) (rolePowerRel *entity.UserRoleRel, exist bool, err error)
}
@ -93,3 +95,12 @@ func (us *UserRoleRelService) GetUserRole(ctx context.Context, userID string) (r
}
return rolePowerRel.RoleID, nil
}
// GetUserByRoleID get user by role id
func (us *UserRoleRelService) GetUserByRoleID(ctx context.Context, roleIDs []int) (rel []*entity.UserRoleRel, err error) {
rolePowerRels, err := us.userRoleRelRepo.GetUserRoleRelListByRoleID(ctx, roleIDs)
if err != nil {
return nil, err
}
return rolePowerRels, nil
}

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"time"
"github.com/answerdev/answer/internal/base/handler"
"github.com/answerdev/answer/internal/base/reason"
@ -12,6 +13,7 @@ import (
"github.com/answerdev/answer/internal/entity"
"github.com/answerdev/answer/internal/schema"
"github.com/answerdev/answer/internal/service/activity"
"github.com/answerdev/answer/internal/service/activity_common"
"github.com/answerdev/answer/internal/service/auth"
"github.com/answerdev/answer/internal/service/export"
"github.com/answerdev/answer/internal/service/role"
@ -31,6 +33,7 @@ type UserService struct {
userCommonService *usercommon.UserCommon
userRepo usercommon.UserRepo
userActivity activity.UserActiveActivityRepo
activityRepo activity_common.ActivityRepo
serviceConfig *service_config.ServiceConfig
emailService *export.EmailService
authService *auth.AuthService
@ -40,6 +43,7 @@ type UserService struct {
func NewUserService(userRepo usercommon.UserRepo,
userActivity activity.UserActiveActivityRepo,
activityRepo activity_common.ActivityRepo,
emailService *export.EmailService,
authService *auth.AuthService,
serviceConfig *service_config.ServiceConfig,
@ -51,6 +55,7 @@ func NewUserService(userRepo usercommon.UserRepo,
userCommonService: userCommonService,
userRepo: userRepo,
userActivity: userActivity,
activityRepo: activityRepo,
emailService: emailService,
serviceConfig: serviceConfig,
authService: authService,
@ -557,7 +562,119 @@ func (us *UserService) getSiteUrl(ctx context.Context) string {
}
// UserRanking get user ranking
func (us *UserService) UserRanking(ctx context.Context) (resp []*schema.UserRankingResp, err error) {
//us.userRepo.GetByUserID()
func (us *UserService) UserRanking(ctx context.Context) (resp *schema.UserRankingResp, err error) {
resp = &schema.UserRankingResp{
UsersWithTheMostReputation: make([]*schema.UserRankingSimpleInfo, 0),
UsersWithTheMostVote: make([]*schema.UserRankingSimpleInfo, 0),
Staffs: make([]*schema.UserRankingSimpleInfo, 0),
}
endTime := time.Now()
startTime := endTime.AddDate(0, 0, -7)
limit := 20
userIDs := make([]string, 0)
userIDExist := make(map[string]bool, 0)
userInfoMapping := make(map[string]*entity.User, 0)
// get most reputation users
rankStat, err := us.activityRepo.GetUsersWhoHasGainedTheMostReputation(ctx, startTime, endTime, limit)
if err != nil {
return nil, err
}
for _, stat := range rankStat {
if stat.Rank == 0 {
continue
}
if userIDExist[stat.UserID] {
continue
}
userIDs = append(userIDs, stat.UserID)
userIDExist[stat.UserID] = true
}
voteStat, err := us.activityRepo.GetUsersWhoHasVoteMost(ctx, startTime, endTime, limit)
if err != nil {
return nil, err
}
for _, stat := range voteStat {
if stat.VoteCount == 0 {
continue
}
if userIDExist[stat.UserID] {
continue
}
userIDs = append(userIDs, stat.UserID)
userIDExist[stat.UserID] = true
}
// get all staff members
userRoleRels, err := us.userRoleService.GetUserByRoleID(ctx, []int{role.RoleAdminID, role.RoleModeratorID})
if err != nil {
return nil, err
}
for _, rel := range userRoleRels {
if userIDExist[rel.UserID] {
continue
}
userIDs = append(userIDs, rel.UserID)
userIDExist[rel.UserID] = true
}
if len(userIDs) == 0 {
return resp, nil
}
userInfoList, err := us.userRepo.BatchGetByID(ctx, userIDs)
if err != nil {
return nil, err
}
for _, user := range userInfoList {
user.Avatar = schema.FormatAvatarInfo(user.Avatar)
userInfoMapping[user.ID] = user
}
for _, stat := range rankStat {
if stat.Rank == 0 {
continue
}
userInfo := userInfoMapping[stat.UserID]
if userInfo == nil {
continue
}
resp.UsersWithTheMostReputation = append(resp.UsersWithTheMostReputation, &schema.UserRankingSimpleInfo{
Username: userInfo.Username,
Rank: stat.Rank,
DisplayName: userInfo.DisplayName,
Avatar: userInfo.Avatar,
})
}
for _, stat := range voteStat {
if stat.VoteCount == 0 {
continue
}
userInfo := userInfoMapping[stat.UserID]
if userInfo == nil {
continue
}
resp.UsersWithTheMostVote = append(resp.UsersWithTheMostVote, &schema.UserRankingSimpleInfo{
Username: userInfo.Username,
VoteCount: stat.VoteCount,
DisplayName: userInfo.DisplayName,
Avatar: userInfo.Avatar,
})
}
for _, rel := range userRoleRels {
userInfo := userInfoMapping[rel.UserID]
if userInfo == nil {
continue
}
resp.Staffs = append(resp.Staffs, &schema.UserRankingSimpleInfo{
Username: userInfo.Username,
Rank: userInfo.Rank,
DisplayName: userInfo.DisplayName,
Avatar: userInfo.Avatar,
})
}
return
}