mirror of https://gitee.com/answerdev/answer.git
feat(user): remove all the content when delete user
This commit is contained in:
parent
133907b59e
commit
b38f2e4345
|
@ -194,7 +194,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
reportAdminService := report_admin.NewReportAdminService(reportRepo, userCommon, answerRepo, questionRepo, commentCommonRepo, reportHandle, configService, objService)
|
||||
controller_adminReportController := controller_admin.NewReportController(reportAdminService)
|
||||
userAdminRepo := user.NewUserAdminRepo(dataData, authRepo)
|
||||
userAdminService := user_admin.NewUserAdminService(userAdminRepo, userRoleRelService, authService, userCommon, userActiveActivityRepo, siteInfoCommonService, emailService)
|
||||
userAdminService := user_admin.NewUserAdminService(userAdminRepo, userRoleRelService, authService, userCommon, userActiveActivityRepo, siteInfoCommonService, emailService, questionRepo, answerRepo, commentCommonRepo)
|
||||
userAdminController := controller_admin.NewUserAdminController(userAdminService)
|
||||
reasonRepo := reason.NewReasonRepo(configService)
|
||||
reasonService := reason2.NewReasonService(reasonRepo)
|
||||
|
|
|
@ -3,6 +3,7 @@ package answer
|
|||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/plugin"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
|
@ -79,6 +80,40 @@ func (ar *answerRepo) RemoveAnswer(ctx context.Context, id string) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// RemoveAllUserAnswer remove all user answer
|
||||
func (ar *answerRepo) RemoveAllUserAnswer(ctx context.Context, userID string) (err error) {
|
||||
// find all answer id that need to be deleted
|
||||
answerIDs := make([]string, 0)
|
||||
session := ar.data.DB.Context(ctx).Where("user_id = ?", userID)
|
||||
session.Where("status != ?", entity.AnswerStatusDeleted)
|
||||
err = session.Select("id").Table("answer").Find(&answerIDs)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if len(answerIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("find %d answers need to be deleted for user %s", len(answerIDs), userID)
|
||||
|
||||
// delete all question
|
||||
session = ar.data.DB.Context(ctx).Where("user_id = ?", userID)
|
||||
session.Where("status != ?", entity.AnswerStatusDeleted)
|
||||
_, err = session.Cols("status", "updated_at").Update(&entity.Answer{
|
||||
UpdatedAt: time.Now(),
|
||||
Status: entity.AnswerStatusDeleted,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
|
||||
// update search content
|
||||
for _, id := range answerIDs {
|
||||
_ = ar.updateSearch(ctx, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAnswer update answer
|
||||
func (ar *answerRepo) UpdateAnswer(ctx context.Context, answer *entity.Answer, Colar []string) (err error) {
|
||||
answer.ID = uid.DeShortID(answer.ID)
|
||||
|
|
|
@ -2,6 +2,7 @@ package comment
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/pager"
|
||||
|
@ -108,3 +109,15 @@ func (cr *commentRepo) GetCommentPage(ctx context.Context, commentQuery *comment
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveAllUserComment remove all user comment
|
||||
func (cr *commentRepo) RemoveAllUserComment(ctx context.Context, userID string) (err error) {
|
||||
session := cr.data.DB.Context(ctx).Where("user_id = ?", userID)
|
||||
session.Where("status != ?", entity.CommentStatusDeleted)
|
||||
affected, err := session.Update(&entity.Comment{Status: entity.CommentStatusDeleted})
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
log.Infof("delete user comment, userID: %s, affected: %d", userID, affected)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -475,3 +475,36 @@ func (qr *questionRepo) updateSearch(ctx context.Context, questionID string) (er
|
|||
err = s.UpdateContent(ctx, content)
|
||||
return
|
||||
}
|
||||
|
||||
func (qr *questionRepo) RemoveAllUserQuestion(ctx context.Context, userID string) (err error) {
|
||||
// get all question id that need to be deleted
|
||||
questionIDs := make([]string, 0)
|
||||
session := qr.data.DB.Context(ctx).Where("user_id = ?", userID)
|
||||
session.Where("status != ?", entity.QuestionStatusDeleted)
|
||||
err = session.Select("id").Table("question").Find(&questionIDs)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if len(questionIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("find %d questions need to be deleted for user %s", len(questionIDs), userID)
|
||||
|
||||
// delete all question
|
||||
session = qr.data.DB.Context(ctx).Where("user_id = ?", userID)
|
||||
session.Where("status != ?", entity.QuestionStatusDeleted)
|
||||
_, err = session.Cols("status", "updated_at").Update(&entity.Question{
|
||||
UpdatedAt: time.Now(),
|
||||
Status: entity.QuestionStatusDeleted,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
|
||||
// update search content
|
||||
for _, id := range questionIDs {
|
||||
_ = qr.updateSearch(ctx, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -13,9 +13,10 @@ import (
|
|||
|
||||
// UpdateUserStatusReq update user request
|
||||
type UpdateUserStatusReq struct {
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
Status string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"`
|
||||
LoginUserID string `json:"-"`
|
||||
UserID string `validate:"required" json:"user_id"`
|
||||
Status string `validate:"required,oneof=normal suspended deleted inactive" json:"status" enums:"normal,suspended,deleted,inactive"`
|
||||
RemoveAllContent bool `validate:"omitempty" json:"remove_all_content"`
|
||||
LoginUserID string `json:"-"`
|
||||
}
|
||||
|
||||
func (r *UpdateUserStatusReq) IsNormal() bool { return r.Status == constant.UserNormal }
|
||||
|
|
|
@ -26,6 +26,7 @@ type AnswerRepo interface {
|
|||
AdminSearchList(ctx context.Context, search *schema.AdminAnswerPageReq) ([]*entity.Answer, int64, error)
|
||||
UpdateAnswerStatus(ctx context.Context, answer *entity.Answer) (err error)
|
||||
GetAnswerCount(ctx context.Context) (count int64, err error)
|
||||
RemoveAllUserAnswer(ctx context.Context, userID string) (err error)
|
||||
}
|
||||
|
||||
// AnswerCommon user service
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
type CommentCommonRepo interface {
|
||||
GetComment(ctx context.Context, commentID string) (comment *entity.Comment, exist bool, err error)
|
||||
GetCommentCount(ctx context.Context) (count int64, err error)
|
||||
RemoveAllUserComment(ctx context.Context, userID string) (err error)
|
||||
}
|
||||
|
||||
// CommentCommonService user service
|
||||
|
|
|
@ -51,6 +51,7 @@ type QuestionRepo interface {
|
|||
GetQuestionCount(ctx context.Context) (count int64, err error)
|
||||
GetUserQuestionCount(ctx context.Context, userID string) (count int64, err error)
|
||||
SitemapQuestions(ctx context.Context, page, pageSize int) (questionIDList []*schema.SiteMapQuestionInfo, err error)
|
||||
RemoveAllUserQuestion(ctx context.Context, userID string) (err error)
|
||||
}
|
||||
|
||||
// QuestionCommon user service
|
||||
|
|
|
@ -27,7 +27,7 @@ type SiteInfoCommonService interface {
|
|||
GetSiteInterface(ctx context.Context) (resp *schema.SiteInterfaceResp, err error)
|
||||
GetSiteBranding(ctx context.Context) (resp *schema.SiteBrandingResp, err error)
|
||||
GetSiteUsers(ctx context.Context) (resp *schema.SiteUsersResp, err error)
|
||||
FormatAvatar(ctx context.Context, originalAvatarData, email string) *schema.AvatarInfo
|
||||
FormatAvatar(ctx context.Context, originalAvatarData, email string, userStatus int) *schema.AvatarInfo
|
||||
FormatListAvatar(ctx context.Context, userList []*entity.User) (userID2AvatarMapping map[string]*schema.AvatarInfo)
|
||||
GetSiteWrite(ctx context.Context) (resp *schema.SiteWriteResp, err error)
|
||||
GetSiteLegal(ctx context.Context) (resp *schema.SiteLegalResp, err error)
|
||||
|
@ -82,9 +82,9 @@ func (s *siteInfoCommonService) GetSiteUsers(ctx context.Context) (resp *schema.
|
|||
}
|
||||
|
||||
// FormatAvatar format avatar
|
||||
func (s *siteInfoCommonService) FormatAvatar(ctx context.Context, originalAvatarData, email string) *schema.AvatarInfo {
|
||||
func (s *siteInfoCommonService) FormatAvatar(ctx context.Context, originalAvatarData, email string, userStatus int) *schema.AvatarInfo {
|
||||
gravatarBaseURL, defaultAvatar := s.getAvatarDefaultConfig(ctx)
|
||||
return s.selectedAvatar(originalAvatarData, defaultAvatar, gravatarBaseURL, email)
|
||||
return s.selectedAvatar(originalAvatarData, defaultAvatar, gravatarBaseURL, email, userStatus)
|
||||
}
|
||||
|
||||
// FormatListAvatar format avatar
|
||||
|
@ -93,7 +93,7 @@ func (s *siteInfoCommonService) FormatListAvatar(ctx context.Context, userList [
|
|||
gravatarBaseURL, defaultAvatar := s.getAvatarDefaultConfig(ctx)
|
||||
avatarMapping = make(map[string]*schema.AvatarInfo)
|
||||
for _, user := range userList {
|
||||
avatarMapping[user.ID] = s.selectedAvatar(user.Avatar, defaultAvatar, gravatarBaseURL, user.EMail)
|
||||
avatarMapping[user.ID] = s.selectedAvatar(user.Avatar, defaultAvatar, gravatarBaseURL, user.EMail, user.Status)
|
||||
}
|
||||
return avatarMapping
|
||||
}
|
||||
|
@ -114,10 +114,18 @@ func (s *siteInfoCommonService) getAvatarDefaultConfig(ctx context.Context) (str
|
|||
}
|
||||
|
||||
func (s *siteInfoCommonService) selectedAvatar(
|
||||
originalAvatarData string, defaultAvatar string, gravatarBaseURL string, email string) *schema.AvatarInfo {
|
||||
originalAvatarData,
|
||||
defaultAvatar, gravatarBaseURL,
|
||||
email string, userStatus int) *schema.AvatarInfo {
|
||||
avatarInfo := &schema.AvatarInfo{}
|
||||
_ = json.Unmarshal([]byte(originalAvatarData), avatarInfo)
|
||||
|
||||
if userStatus == entity.UserStatusDeleted {
|
||||
return &schema.AvatarInfo{
|
||||
Type: constant.DefaultAvatar,
|
||||
}
|
||||
}
|
||||
|
||||
if len(avatarInfo.Type) == 0 && defaultAvatar == constant.AvatarTypeGravatar {
|
||||
avatarInfo.Type = constant.AvatarTypeGravatar
|
||||
avatarInfo.Gravatar = gravatar.GetAvatarURL(gravatarBaseURL, email)
|
||||
|
|
|
@ -7,7 +7,10 @@ import (
|
|||
"github.com/answerdev/answer/internal/base/handler"
|
||||
"github.com/answerdev/answer/internal/base/translator"
|
||||
"github.com/answerdev/answer/internal/base/validator"
|
||||
answercommon "github.com/answerdev/answer/internal/service/answer_common"
|
||||
"github.com/answerdev/answer/internal/service/comment_common"
|
||||
"github.com/answerdev/answer/internal/service/export"
|
||||
questioncommon "github.com/answerdev/answer/internal/service/question_common"
|
||||
"github.com/google/uuid"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
@ -50,6 +53,9 @@ type UserAdminService struct {
|
|||
userActivity activity.UserActiveActivityRepo
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService
|
||||
emailService *export.EmailService
|
||||
questionCommonRepo questioncommon.QuestionRepo
|
||||
answerCommonRepo answercommon.AnswerRepo
|
||||
commentCommonRepo comment_common.CommentCommonRepo
|
||||
}
|
||||
|
||||
// NewUserAdminService new user admin service
|
||||
|
@ -61,6 +67,9 @@ func NewUserAdminService(
|
|||
userActivity activity.UserActiveActivityRepo,
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService,
|
||||
emailService *export.EmailService,
|
||||
questionCommonRepo questioncommon.QuestionRepo,
|
||||
answerCommonRepo answercommon.AnswerRepo,
|
||||
commentCommonRepo comment_common.CommentCommonRepo,
|
||||
) *UserAdminService {
|
||||
return &UserAdminService{
|
||||
userRepo: userRepo,
|
||||
|
@ -70,6 +79,9 @@ func NewUserAdminService(
|
|||
userActivity: userActivity,
|
||||
siteInfoCommonService: siteInfoCommonService,
|
||||
emailService: emailService,
|
||||
questionCommonRepo: questionCommonRepo,
|
||||
answerCommonRepo: answerCommonRepo,
|
||||
commentCommonRepo: commentCommonRepo,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +123,11 @@ func (us *UserAdminService) UpdateUserStatus(ctx context.Context, req *schema.Up
|
|||
return err
|
||||
}
|
||||
|
||||
// remove all content that user created, such as question, answer, comment, etc.
|
||||
if req.RemoveAllContent {
|
||||
us.removeAllUserCreatedContent(ctx, userInfo.ID)
|
||||
}
|
||||
|
||||
// if user reputation is zero means this user is inactive, so try to activate this user.
|
||||
if req.IsNormal() && userInfo.Rank == 0 {
|
||||
return us.userActivity.UserActive(ctx, userInfo.ID)
|
||||
|
@ -118,6 +135,19 @@ func (us *UserAdminService) UpdateUserStatus(ctx context.Context, req *schema.Up
|
|||
return nil
|
||||
}
|
||||
|
||||
// removeAllUserCreatedContent remove all user created content
|
||||
func (us *UserAdminService) removeAllUserCreatedContent(ctx context.Context, userID string) {
|
||||
if err := us.questionCommonRepo.RemoveAllUserQuestion(ctx, userID); err != nil {
|
||||
log.Errorf("remove all user question error: %v", err)
|
||||
}
|
||||
if err := us.answerCommonRepo.RemoveAllUserAnswer(ctx, userID); err != nil {
|
||||
log.Errorf("remove all user answer error: %v", err)
|
||||
}
|
||||
if err := us.commentCommonRepo.RemoveAllUserComment(ctx, userID); err != nil {
|
||||
log.Errorf("remove all user comment error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateUserRole update user role
|
||||
func (us *UserAdminService) UpdateUserRole(ctx context.Context, req *schema.UpdateUserRoleReq) (err error) {
|
||||
// Users cannot modify their roles
|
||||
|
|
|
@ -69,7 +69,7 @@ func (us *UserCommon) GetUserBasicInfoByID(ctx context.Context, ID string) (
|
|||
return nil, exist, err
|
||||
}
|
||||
info := us.FormatUserBasicInfo(ctx, userInfo)
|
||||
info.Avatar = us.siteInfoCommonService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
info.Avatar = us.siteInfoCommonService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
return info, exist, nil
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ func (us *UserCommon) GetUserBasicInfoByUserName(ctx context.Context, username s
|
|||
return nil, exist, err
|
||||
}
|
||||
info := us.FormatUserBasicInfo(ctx, userInfo)
|
||||
info.Avatar = us.siteInfoCommonService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
info.Avatar = us.siteInfoCommonService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
return info, exist, nil
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity.
|
|||
userBasicInfo.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus)
|
||||
if userBasicInfo.Status == constant.UserDeleted {
|
||||
userBasicInfo.Avatar = ""
|
||||
userBasicInfo.DisplayName = "Anonymous"
|
||||
userBasicInfo.DisplayName = "user" + userInfo.ID
|
||||
}
|
||||
return userBasicInfo
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ func (us *UserService) GetUserInfoByUserID(ctx context.Context, token, userID st
|
|||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail)
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status)
|
||||
resp.AccessToken = token
|
||||
resp.HavePassword = len(userInfo.Pass) > 0
|
||||
return resp, nil
|
||||
|
@ -109,7 +109,7 @@ func (us *UserService) GetOtherUserInfoByUsername(ctx context.Context, username
|
|||
}
|
||||
resp = &schema.GetOtherUserInfoByUsernameResp{}
|
||||
resp.ConvertFromUserEntity(userInfo)
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ func (us *UserService) EmailLogin(ctx context.Context, req *schema.UserEmailLogi
|
|||
|
||||
resp = &schema.UserLoginResp{}
|
||||
resp.ConvertFromUserEntity(userInfo)
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
userCacheInfo := &entity.UserCacheInfo{
|
||||
UserID: userInfo.ID,
|
||||
EmailStatus: userInfo.MailStatus,
|
||||
|
@ -426,7 +426,7 @@ func (us *UserService) UserRegisterByEmail(ctx context.Context, registerUserInfo
|
|||
// return user info and token
|
||||
resp = &schema.UserLoginResp{}
|
||||
resp.ConvertFromUserEntity(userInfo)
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
userCacheInfo := &entity.UserCacheInfo{
|
||||
UserID: userInfo.ID,
|
||||
EmailStatus: userInfo.MailStatus,
|
||||
|
@ -511,7 +511,7 @@ func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVeri
|
|||
|
||||
resp = &schema.UserLoginResp{}
|
||||
resp.ConvertFromUserEntity(userInfo)
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
resp.AccessToken = accessToken
|
||||
// User verified email will update user email status. So user status cache should be updated.
|
||||
if err = us.authService.SetUserStatus(ctx, userCacheInfo); err != nil {
|
||||
|
@ -630,7 +630,7 @@ func (us *UserService) UserChangeEmailVerify(ctx context.Context, content string
|
|||
|
||||
resp = &schema.UserLoginResp{}
|
||||
resp.ConvertFromUserEntity(userInfo)
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail).GetURL()
|
||||
resp.Avatar = us.siteInfoService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
userCacheInfo := &entity.UserCacheInfo{
|
||||
UserID: userInfo.ID,
|
||||
EmailStatus: entity.EmailStatusAvailable,
|
||||
|
|
Loading…
Reference in New Issue