chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package usercommon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/apache/answer/internal/base/constant"
|
||||
"github.com/apache/answer/pkg/converter"
|
||||
|
||||
"github.com/apache/answer/internal/base/reason"
|
||||
"github.com/apache/answer/internal/entity"
|
||||
"github.com/apache/answer/internal/schema"
|
||||
"github.com/apache/answer/internal/service/auth"
|
||||
"github.com/apache/answer/internal/service/role"
|
||||
"github.com/apache/answer/internal/service/siteinfo_common"
|
||||
"github.com/apache/answer/pkg/checker"
|
||||
"github.com/apache/answer/pkg/random"
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
type UserRepo interface {
|
||||
AddUser(ctx context.Context, user *entity.User) (err error)
|
||||
IncreaseAnswerCount(ctx context.Context, userID string, amount int) (err error)
|
||||
IncreaseQuestionCount(ctx context.Context, userID string, amount int) (err error)
|
||||
UpdateQuestionCount(ctx context.Context, userID string, count int64) (err error)
|
||||
UpdateAnswerCount(ctx context.Context, userID string, count int) (err error)
|
||||
UpdateLastLoginDate(ctx context.Context, userID string) (err error)
|
||||
UpdateEmailStatus(ctx context.Context, userID string, emailStatus int) error
|
||||
UpdateNoticeStatus(ctx context.Context, userID string, noticeStatus int) error
|
||||
UpdateEmail(ctx context.Context, userID, email string) error
|
||||
UpdateUserInterface(ctx context.Context, userID, language, colorSchema string) (err error)
|
||||
UpdatePass(ctx context.Context, userID, pass string) error
|
||||
UpdateInfo(ctx context.Context, userInfo *entity.User) (err error)
|
||||
UpdateUserProfile(ctx context.Context, userInfo *entity.User) (err error)
|
||||
GetByUserID(ctx context.Context, userID string) (userInfo *entity.User, exist bool, err error)
|
||||
BatchGetByID(ctx context.Context, ids []string) ([]*entity.User, error)
|
||||
GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error)
|
||||
GetByUsernames(ctx context.Context, usernames []string) ([]*entity.User, error)
|
||||
GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error)
|
||||
GetUserCount(ctx context.Context) (count int64, err error)
|
||||
SearchUserListByName(ctx context.Context, name string, limit int, onlyStaff bool) (userList []*entity.User, err error)
|
||||
IsAvatarFileUsed(ctx context.Context, filePath string) (bool, error)
|
||||
}
|
||||
|
||||
// UserCommon user service
|
||||
type UserCommon struct {
|
||||
userRepo UserRepo
|
||||
userRoleService *role.UserRoleRelService
|
||||
authService *auth.AuthService
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService
|
||||
}
|
||||
|
||||
func NewUserCommon(
|
||||
userRepo UserRepo,
|
||||
userRoleService *role.UserRoleRelService,
|
||||
authService *auth.AuthService,
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService,
|
||||
) *UserCommon {
|
||||
return &UserCommon{
|
||||
userRepo: userRepo,
|
||||
userRoleService: userRoleService,
|
||||
authService: authService,
|
||||
siteInfoCommonService: siteInfoCommonService,
|
||||
}
|
||||
}
|
||||
|
||||
func (us *UserCommon) GetUserBasicInfoByID(ctx context.Context, id string) (
|
||||
userBasicInfo *schema.UserBasicInfo, exist bool, err error) {
|
||||
userInfo, exist, err := us.userRepo.GetByUserID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, exist, err
|
||||
}
|
||||
info := us.FormatUserBasicInfo(ctx, userInfo)
|
||||
info.Avatar = us.siteInfoCommonService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
return info, exist, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) GetUserBasicInfoByUserName(ctx context.Context, username string) (*schema.UserBasicInfo, bool, error) {
|
||||
userInfo, exist, err := us.userRepo.GetByUsername(ctx, username)
|
||||
if err != nil {
|
||||
return nil, exist, err
|
||||
}
|
||||
info := us.FormatUserBasicInfo(ctx, userInfo)
|
||||
info.Avatar = us.siteInfoCommonService.FormatAvatar(ctx, userInfo.Avatar, userInfo.EMail, userInfo.Status).GetURL()
|
||||
return info, exist, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) BatchGetUserBasicInfoByUserNames(ctx context.Context, usernames []string) (map[string]*schema.UserBasicInfo, error) {
|
||||
infomap := make(map[string]*schema.UserBasicInfo)
|
||||
list, err := us.userRepo.GetByUsernames(ctx, usernames)
|
||||
if err != nil {
|
||||
return infomap, err
|
||||
}
|
||||
avatarMapping := us.siteInfoCommonService.FormatListAvatar(ctx, list)
|
||||
for _, user := range list {
|
||||
info := us.FormatUserBasicInfo(ctx, user)
|
||||
info.Avatar = avatarMapping[user.ID].GetURL()
|
||||
infomap[user.Username] = info
|
||||
}
|
||||
return infomap, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error) {
|
||||
return us.userRepo.GetByEmail(ctx, email)
|
||||
}
|
||||
|
||||
func (us *UserCommon) GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error) {
|
||||
return us.userRepo.GetByUsername(ctx, username)
|
||||
}
|
||||
|
||||
func (us *UserCommon) UpdateUserProfile(ctx context.Context, userInfo *entity.User) (err error) {
|
||||
return us.userRepo.UpdateUserProfile(ctx, userInfo)
|
||||
}
|
||||
|
||||
func (us *UserCommon) UpdateAnswerCount(ctx context.Context, userID string, num int) error {
|
||||
return us.userRepo.UpdateAnswerCount(ctx, userID, num)
|
||||
}
|
||||
|
||||
func (us *UserCommon) UpdateQuestionCount(ctx context.Context, userID string, num int64) error {
|
||||
return us.userRepo.UpdateQuestionCount(ctx, userID, num)
|
||||
}
|
||||
|
||||
func (us *UserCommon) BatchUserBasicInfoByID(ctx context.Context, userIDs []string) (map[string]*schema.UserBasicInfo, error) {
|
||||
userIDs = checker.FilterEmptyString(userIDs)
|
||||
userMap := make(map[string]*schema.UserBasicInfo)
|
||||
if len(userIDs) == 0 {
|
||||
return userMap, nil
|
||||
}
|
||||
userList, err := us.userRepo.BatchGetByID(ctx, userIDs)
|
||||
if err != nil {
|
||||
return userMap, err
|
||||
}
|
||||
avatarMapping := us.siteInfoCommonService.FormatListAvatar(ctx, userList)
|
||||
for _, user := range userList {
|
||||
info := us.FormatUserBasicInfo(ctx, user)
|
||||
info.Avatar = avatarMapping[user.ID].GetURL()
|
||||
userMap[user.ID] = info
|
||||
}
|
||||
for _, id := range userIDs {
|
||||
if _, ok := userMap[id]; !ok {
|
||||
userMap[id] = &schema.UserBasicInfo{
|
||||
ID: id,
|
||||
DisplayName: "user" + converter.DeleteUserDisplay(id),
|
||||
Status: constant.UserDeleted,
|
||||
}
|
||||
}
|
||||
}
|
||||
return userMap, nil
|
||||
}
|
||||
|
||||
// FormatUserBasicInfo format user basic info
|
||||
func (us *UserCommon) FormatUserBasicInfo(ctx context.Context, userInfo *entity.User) *schema.UserBasicInfo {
|
||||
userBasicInfo := &schema.UserBasicInfo{}
|
||||
userBasicInfo.ID = userInfo.ID
|
||||
userBasicInfo.Username = userInfo.Username
|
||||
userBasicInfo.Rank = userInfo.Rank
|
||||
userBasicInfo.DisplayName = userInfo.DisplayName
|
||||
userBasicInfo.Website = userInfo.Website
|
||||
userBasicInfo.Location = userInfo.Location
|
||||
userBasicInfo.Language = userInfo.Language
|
||||
userBasicInfo.Status = constant.ConvertUserStatus(userInfo.Status, userInfo.MailStatus)
|
||||
if !userInfo.SuspendedUntil.IsZero() {
|
||||
userBasicInfo.SuspendedUntil = userInfo.SuspendedUntil.Unix()
|
||||
}
|
||||
if userBasicInfo.Status == constant.UserDeleted {
|
||||
userBasicInfo.Avatar = ""
|
||||
userBasicInfo.DisplayName = "user" + converter.DeleteUserDisplay(userInfo.ID)
|
||||
}
|
||||
return userBasicInfo
|
||||
}
|
||||
|
||||
// MakeUsername
|
||||
// Generate a unique Username based on the displayName
|
||||
func (us *UserCommon) MakeUsername(ctx context.Context, displayName string) (username string, err error) {
|
||||
// Chinese processing
|
||||
if has := checker.IsChinese(displayName); has {
|
||||
displayName = strings.Join(pinyin.LazyConvert(displayName, nil), "")
|
||||
}
|
||||
|
||||
username = strings.ReplaceAll(displayName, " ", "-")
|
||||
username = strings.ToLower(username)
|
||||
suffix := ""
|
||||
|
||||
if checker.IsInvalidUsername(username) {
|
||||
return "", errors.BadRequest(reason.UsernameInvalid)
|
||||
}
|
||||
|
||||
if checker.IsReservedUsername(username) {
|
||||
return "", errors.BadRequest(reason.UsernameInvalid)
|
||||
}
|
||||
|
||||
for {
|
||||
_, has, err := us.userRepo.GetByUsername(ctx, username+suffix)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !has {
|
||||
break
|
||||
}
|
||||
suffix = random.UsernameSuffix()
|
||||
}
|
||||
return username + suffix, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) CacheLoginUserInfo(ctx context.Context, userID string, userStatus, emailStatus int, externalID string) (
|
||||
accessToken string, userCacheInfo *entity.UserCacheInfo, err error) {
|
||||
roleID, err := us.userRoleService.GetUserRole(ctx, userID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
userCacheInfo = &entity.UserCacheInfo{
|
||||
UserID: userID,
|
||||
EmailStatus: emailStatus,
|
||||
UserStatus: userStatus,
|
||||
RoleID: roleID,
|
||||
ExternalID: externalID,
|
||||
}
|
||||
|
||||
accessToken, _, err = us.authService.SetUserCacheInfo(ctx, userCacheInfo)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if userCacheInfo.RoleID == role.RoleAdminID {
|
||||
if err = us.authService.SetAdminUserCacheInfo(ctx, accessToken, userCacheInfo); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
return accessToken, userCacheInfo, nil
|
||||
}
|
||||
|
||||
func (us *UserCommon) IsAvatarFileUsed(ctx context.Context, filePath string) bool {
|
||||
used, err := us.userRepo.IsAvatarFileUsed(ctx, filePath)
|
||||
if err != nil {
|
||||
log.Errorf("error checking if branding file is used: %v", err)
|
||||
// will try again with the next clean up
|
||||
return true
|
||||
}
|
||||
return used
|
||||
}
|
||||
Reference in New Issue
Block a user