588 lines
18 KiB
Go
588 lines
18 KiB
Go
package authser
|
||
|
||
import (
|
||
"context"
|
||
"math/rand"
|
||
"strconv"
|
||
"time"
|
||
"videoweb/app/appg"
|
||
"videoweb/app/service/configser"
|
||
"videoweb/pub/common/enum/consts"
|
||
"videoweb/pub/common/log"
|
||
"videoweb/pub/common/netutil"
|
||
"videoweb/pub/common/srvdef"
|
||
"videoweb/pub/common/utils"
|
||
"videoweb/pub/models/usermod"
|
||
"videoweb/pub/mongos"
|
||
"videoweb/pub/pubdef"
|
||
"videoweb/pub/redis"
|
||
|
||
"go.mongodb.org/mongo-driver/bson"
|
||
"go.mongodb.org/mongo-driver/mongo/options"
|
||
"golang.org/x/crypto/bcrypt"
|
||
)
|
||
|
||
var tokener = utils.NewTokenRedis()
|
||
|
||
var defaultAvatars = []string{
|
||
"v3/image/bo/ty/1016/girl1.jpeg",
|
||
"v3/image/re/9y/1016/girl2.jpeg",
|
||
"v3/image/5f/k4/1016/girl3.jpeg",
|
||
"v3/image/ej/ss/1016/girl4.jpeg",
|
||
"v3/image/yq/ys/1016/girl5.jpeg",
|
||
"v3/image/9e/d3/1016/girl6.jpeg",
|
||
}
|
||
|
||
func randomAvatar() string {
|
||
return defaultAvatars[time.Now().UnixNano()%int64(len(defaultAvatars))]
|
||
}
|
||
|
||
// generateUniqueInviteCode 生成 8 位唯一邀请码(含大小写字母),最多重试 10 次
|
||
func generateUniqueInviteCode(ctx context.Context) string {
|
||
for i := 0; i < 10; i++ {
|
||
code := utils.GetRandomLetters(8)
|
||
exist, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.InviteCode: code})
|
||
if exist == nil {
|
||
return code
|
||
}
|
||
log.Warn("authser:generateUniqueInviteCode collision, retrying", log.Any("code", code))
|
||
}
|
||
// 极小概率走到这里:用更长的随机串兜底
|
||
return utils.GetRandomLetters(12)
|
||
}
|
||
|
||
// LoginResp 登录/注册成功后的响应数据
|
||
type LoginResp struct {
|
||
Token string `json:"token"`
|
||
UserId int64 `json:"userId"`
|
||
AccountNo string `json:"accountNo"`
|
||
NickName string `json:"nickName"`
|
||
Status pubdef.UserStatus `json:"status"`
|
||
Avatar string `json:"avatar"` // 头像
|
||
InviteCode string `json:"inviteCode"` // 邀请码
|
||
LoginType pubdef.LoginType `json:"loginType"` // 登录方式 0=设备 3=账号
|
||
IsRegister bool `json:"isRegister"` // 是否已完成账号注册
|
||
IsNewUser bool `json:"isNewUser"` // 是否本次请求新创建的用户
|
||
Recharge int64 `json:"recharge"` // 当前金币个数
|
||
}
|
||
|
||
// UserInfoResp 当前登录用户信息
|
||
type UserInfoResp struct {
|
||
UserId int64 `json:"userId"`
|
||
AccountNo string `json:"accountNo"`
|
||
NickName string `json:"nickName"`
|
||
Status pubdef.UserStatus `json:"status"`
|
||
Avatar string `json:"avatar"`
|
||
InviteCode string `json:"inviteCode"`
|
||
LoginType pubdef.LoginType `json:"loginType"`
|
||
IsRegister bool `json:"isRegister"`
|
||
Recharge int64 `json:"recharge"`
|
||
}
|
||
|
||
// UAInfo 从 UA 中间件提取的设备/系统信息
|
||
type UAInfo struct {
|
||
Device string // 设备型号(DevType,如 "iPhone")
|
||
SysType pubdef.SysType // 系统类型(ios / android / h5 等)
|
||
AppVer string // App 版本号
|
||
IP string // 客户端 IP
|
||
}
|
||
|
||
// GuestLoginReq 游客登录请求
|
||
type GuestLoginReq struct {
|
||
DevID string // 设备 ID(首选)
|
||
Mac string // 设备 MAC 地址(备用)
|
||
DeviceId string // 设备 ID 第二版本(备用)
|
||
UA UAInfo // 设备/系统信息
|
||
}
|
||
|
||
// hashPassword 使用 bcrypt 对明文密码进行哈希
|
||
func hashPassword(plain string) (string, error) {
|
||
hash, err := bcrypt.GenerateFromPassword([]byte(plain), bcrypt.DefaultCost)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
return string(hash), nil
|
||
}
|
||
|
||
// checkPassword 验证明文密码与 bcrypt 哈希是否匹配
|
||
func checkPassword(plain, hash string) bool {
|
||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(plain)) == nil
|
||
}
|
||
|
||
// generateAndSaveToken 生成 JWT 并存入 Redis
|
||
func generateAndSaveToken(userId int64) (string, *srvdef.SrvErr) {
|
||
token, err := utils.GenerateUserToken(utils.UserClaims{
|
||
UserID: userId,
|
||
Expire: consts.UserJwtTokenExpire,
|
||
}, appg.Conf.JwtKey)
|
||
if err != nil {
|
||
return "", srvdef.New(srvdef.GenerateTokenFailed, srvdef.GenerateTokenFailed.Msg(), "")
|
||
}
|
||
if err = tokener.Save(userId, token); err != nil {
|
||
return "", srvdef.New(srvdef.StoreLevel, "token存储失败", "")
|
||
}
|
||
return token, nil
|
||
}
|
||
|
||
// GuestLogin 游客登录:按设备 ID 查找已有用户,找不到则自动注册
|
||
// 若请求携带有效 token(currentUserId != 0),优先复用该账号
|
||
func GuestLogin(ctx context.Context, req *GuestLoginReq, currentUserId int64) (*LoginResp, *srvdef.SrvErr) {
|
||
var user *usermod.User
|
||
|
||
// 优先用 token 对应的已有用户
|
||
if currentUserId > 0 {
|
||
u, _ := usermod.FindOneUserById(ctx, currentUserId)
|
||
if u != nil {
|
||
user = u
|
||
}
|
||
}
|
||
|
||
// 按设备 ID 查找
|
||
if user == nil {
|
||
user, _ = findUserByDevice(ctx, req.DevID, req.Mac, req.DeviceId)
|
||
}
|
||
|
||
isNewUser := false
|
||
if user == nil {
|
||
// 未找到 → 自动注册游客
|
||
var serr *srvdef.SrvErr
|
||
user, serr = registerGuest(ctx, req)
|
||
if serr != nil {
|
||
return nil, serr
|
||
}
|
||
isNewUser = true
|
||
} else {
|
||
// 已有用户:更新设备信息和 UA(静默,不影响主流程)
|
||
updateSet := bson.M{
|
||
usermod.FN.User.LastLoginAt: time.Now(),
|
||
usermod.FN.User.UpdatedAt: time.Now(),
|
||
}
|
||
if req.DevID != "" {
|
||
updateSet[usermod.FN.User.DevID] = req.DevID
|
||
}
|
||
if req.Mac != "" && req.Mac != "00:00:00:00:00:00" {
|
||
updateSet[usermod.FN.User.Mac] = req.Mac
|
||
}
|
||
if req.DeviceId != "" {
|
||
updateSet[usermod.FN.User.DevV2] = req.DeviceId
|
||
}
|
||
if req.UA.Device != "" {
|
||
updateSet[usermod.FN.User.Device] = req.UA.Device
|
||
}
|
||
if req.UA.SysType != "" {
|
||
updateSet[usermod.FN.User.SysType] = req.UA.SysType.String()
|
||
}
|
||
if req.UA.AppVer != "" {
|
||
updateSet[usermod.FN.User.AppVer] = req.UA.AppVer
|
||
}
|
||
if req.UA.IP != "" {
|
||
updateSet[usermod.FN.User.LastIp] = req.UA.IP
|
||
}
|
||
_ = usermod.UpdateOneUser(ctx,
|
||
bson.M{usermod.FN.User.Id: user.Id},
|
||
bson.M{"$set": updateSet},
|
||
)
|
||
}
|
||
|
||
if user.Status == pubdef.UserStatusDisable {
|
||
return nil, srvdef.New(srvdef.AccountErrAccessForbid, srvdef.AccountErrAccessForbid.Msg(), "")
|
||
}
|
||
|
||
token, serr := generateAndSaveToken(user.Id)
|
||
if serr != nil {
|
||
return nil, serr
|
||
}
|
||
cdn := configser.ImageCdnFromCtx(ctx)
|
||
return &LoginResp{
|
||
Token: token,
|
||
UserId: user.Id,
|
||
AccountNo: user.AccountNo,
|
||
NickName: user.NickName,
|
||
Status: user.Status,
|
||
Avatar: netutil.MergeURL(cdn, user.Avatar),
|
||
InviteCode: user.InviteCode,
|
||
LoginType: user.LoginType,
|
||
IsRegister: user.IsRegister,
|
||
IsNewUser: isNewUser,
|
||
Recharge: user.Recharge,
|
||
}, nil
|
||
}
|
||
|
||
// GetInfo 根据当前登录 token 解析出的用户 ID 查询用户信息
|
||
func GetInfo(ctx context.Context, userId int64) (*UserInfoResp, *srvdef.SrvErr) {
|
||
if userId <= 0 {
|
||
return nil, srvdef.New(srvdef.InvalidToken, srvdef.InvalidToken.Msg(), "")
|
||
}
|
||
user, serr := usermod.FindOneUserById(ctx, userId, options.FindOne().SetProjection(userInfoProjection()))
|
||
if serr != nil {
|
||
return nil, serr
|
||
}
|
||
if user == nil {
|
||
return nil, srvdef.New(srvdef.ErrUserNotExsit, srvdef.ErrUserNotExsit.Msg(), "")
|
||
}
|
||
if user.Status == pubdef.UserStatusDisable {
|
||
return nil, srvdef.New(srvdef.AccountErrAccessForbid, srvdef.AccountErrAccessForbid.Msg(), "")
|
||
}
|
||
cdn := configser.ImageCdnFromCtx(ctx)
|
||
return &UserInfoResp{
|
||
UserId: user.Id,
|
||
AccountNo: user.AccountNo,
|
||
NickName: user.NickName,
|
||
Status: user.Status,
|
||
Avatar: netutil.MergeURL(cdn, user.Avatar),
|
||
InviteCode: user.InviteCode,
|
||
LoginType: user.LoginType,
|
||
IsRegister: user.IsRegister,
|
||
Recharge: user.Recharge,
|
||
}, nil
|
||
}
|
||
|
||
func userInfoProjection() bson.M {
|
||
return bson.M{
|
||
usermod.FN.User.Id: 1,
|
||
usermod.FN.User.AccountNo: 1,
|
||
usermod.FN.User.NickName: 1,
|
||
usermod.FN.User.Status: 1,
|
||
usermod.FN.User.Avatar: 1,
|
||
usermod.FN.User.InviteCode: 1,
|
||
usermod.FN.User.LoginType: 1,
|
||
usermod.FN.User.IsRegister: 1,
|
||
usermod.FN.User.Recharge: 1,
|
||
}
|
||
}
|
||
|
||
// findUserByDevice 按设备 ID 三级回退查找用户(DevID → Mac → DeviceId)
|
||
func findUserByDevice(ctx context.Context, devID, mac, deviceId string) (*usermod.User, *srvdef.SrvErr) {
|
||
invalidMac := "00:00:00:00:00:00"
|
||
if devID != "" {
|
||
u, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.DevID: devID})
|
||
if u != nil {
|
||
return u, nil
|
||
}
|
||
}
|
||
if mac != "" && mac != invalidMac {
|
||
u, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Mac: mac})
|
||
if u != nil {
|
||
return u, nil
|
||
}
|
||
}
|
||
if deviceId != "" && deviceId != invalidMac {
|
||
u, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.DevV2: deviceId})
|
||
if u != nil {
|
||
return u, nil
|
||
}
|
||
}
|
||
return nil, nil
|
||
}
|
||
|
||
// registerGuest 自动创建游客用户
|
||
func registerGuest(ctx context.Context, req *GuestLoginReq) (*usermod.User, *srvdef.SrvErr) {
|
||
now := time.Now()
|
||
user := &usermod.User{
|
||
Id: mongos.Lid.User.Get(),
|
||
NickName: getName(),
|
||
Avatar: randomAvatar(),
|
||
InviteCode: generateUniqueInviteCode(ctx),
|
||
Status: pubdef.UserStatusNormal,
|
||
LoginType: pubdef.LoginTypeDevice,
|
||
IsRegister: false,
|
||
DevID: req.DevID,
|
||
Mac: req.Mac,
|
||
DevV2: req.DeviceId,
|
||
Device: req.UA.Device,
|
||
SysType: req.UA.SysType.String(),
|
||
AppVer: req.UA.AppVer,
|
||
OriVer: req.UA.AppVer,
|
||
RegIp: req.UA.IP,
|
||
LastIp: req.UA.IP,
|
||
RegisterTime: now,
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
}
|
||
if serr := usermod.InsertOneUser(ctx, user); serr != nil {
|
||
return nil, serr
|
||
}
|
||
return user, nil
|
||
}
|
||
|
||
// Register 账号注册
|
||
// currentUserId != 0 时为游客升级模式:在已有游客账号上绑定账号密码
|
||
// currentUserId == 0 时为直接注册新账号
|
||
func Register(ctx context.Context, accountNo, password, agentInviteCode string, currentUserId int64, ua UAInfo) (*LoginResp, *srvdef.SrvErr) {
|
||
// 检查 accountNo 是否已被其他用户占用
|
||
exist, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.AccountNo: accountNo})
|
||
if exist != nil && exist.Id != currentUserId {
|
||
return nil, srvdef.New(srvdef.ErrDbInputExist, "账号已存在", "")
|
||
}
|
||
|
||
hashed, err := hashPassword(password)
|
||
if err != nil {
|
||
return nil, srvdef.New(srvdef.Failure, "密码处理失败", "")
|
||
}
|
||
|
||
now := time.Now()
|
||
|
||
// 游客升级:更新已有记录
|
||
if currentUserId > 0 {
|
||
user, _ := usermod.FindOneUserById(ctx, currentUserId)
|
||
if user == nil {
|
||
return nil, srvdef.New(srvdef.ErrUserNotExsit, "用户不存在", "")
|
||
}
|
||
if user.IsRegister {
|
||
return nil, srvdef.New(srvdef.ErrDbInputExist, "已完成账号注册,无需重复操作", "")
|
||
}
|
||
setDoc := bson.M{
|
||
usermod.FN.User.AccountNo: accountNo,
|
||
usermod.FN.User.Password: hashed,
|
||
usermod.FN.User.IsRegister: true,
|
||
usermod.FN.User.LoginType: pubdef.LoginTypeAccount,
|
||
usermod.FN.User.UpdatedAt: now,
|
||
}
|
||
if agentInviteCode != "" && user.AgentInviteCode == "" {
|
||
setDoc[usermod.FN.User.AgentInviteCode] = agentInviteCode
|
||
}
|
||
if ua.Device != "" {
|
||
setDoc[usermod.FN.User.Device] = ua.Device
|
||
}
|
||
if ua.SysType != "" {
|
||
setDoc[usermod.FN.User.SysType] = ua.SysType.String()
|
||
}
|
||
if ua.AppVer != "" {
|
||
setDoc[usermod.FN.User.AppVer] = ua.AppVer
|
||
}
|
||
if ua.IP != "" {
|
||
setDoc[usermod.FN.User.LastIp] = ua.IP
|
||
}
|
||
if serr := usermod.UpdateOneUser(ctx,
|
||
bson.M{usermod.FN.User.Id: currentUserId},
|
||
bson.M{"$set": setDoc},
|
||
); serr != nil {
|
||
return nil, serr
|
||
}
|
||
// 给邀请人 +1
|
||
if agentInviteCode != "" {
|
||
_ = usermod.UpdateOneUser(ctx,
|
||
bson.M{usermod.FN.User.InviteCode: agentInviteCode},
|
||
bson.M{"$inc": bson.M{usermod.FN.User.Invites: 1},
|
||
"$set": bson.M{usermod.FN.User.UpdatedAt: time.Now()}},
|
||
)
|
||
}
|
||
token, serr := generateAndSaveToken(user.Id)
|
||
if serr != nil {
|
||
return nil, serr
|
||
}
|
||
cdn := configser.ImageCdnFromCtx(ctx)
|
||
return &LoginResp{
|
||
Token: token,
|
||
UserId: user.Id,
|
||
AccountNo: accountNo,
|
||
NickName: user.NickName,
|
||
Status: user.Status,
|
||
Avatar: netutil.MergeURL(cdn, user.Avatar),
|
||
InviteCode: user.InviteCode,
|
||
LoginType: pubdef.LoginTypeAccount,
|
||
IsRegister: true,
|
||
Recharge: user.Recharge,
|
||
}, nil
|
||
}
|
||
|
||
// 未登录:直接注册新账号
|
||
user := &usermod.User{
|
||
Id: mongos.Lid.User.Get(),
|
||
AccountNo: accountNo,
|
||
Password: hashed,
|
||
NickName: getName(),
|
||
Avatar: randomAvatar(),
|
||
InviteCode: generateUniqueInviteCode(ctx),
|
||
AgentInviteCode: agentInviteCode,
|
||
Status: pubdef.UserStatusNormal,
|
||
IsRegister: true,
|
||
LoginType: pubdef.LoginTypeAccount,
|
||
Device: ua.Device,
|
||
SysType: ua.SysType.String(),
|
||
AppVer: ua.AppVer,
|
||
OriVer: ua.AppVer,
|
||
RegIp: ua.IP,
|
||
LastIp: ua.IP,
|
||
RegisterTime: now,
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
}
|
||
if serr := usermod.InsertOneUser(ctx, user); serr != nil {
|
||
return nil, serr
|
||
}
|
||
if agentInviteCode != "" {
|
||
_ = usermod.UpdateOneUser(ctx,
|
||
bson.M{usermod.FN.User.InviteCode: agentInviteCode},
|
||
bson.M{
|
||
"$inc": bson.M{usermod.FN.User.Invites: 1},
|
||
"$set": bson.M{usermod.FN.User.UpdatedAt: time.Now()},
|
||
},
|
||
)
|
||
}
|
||
token, serr := generateAndSaveToken(user.Id)
|
||
if serr != nil {
|
||
return nil, serr
|
||
}
|
||
cdn := configser.ImageCdnFromCtx(ctx)
|
||
return &LoginResp{
|
||
Token: token,
|
||
UserId: user.Id,
|
||
AccountNo: user.AccountNo,
|
||
NickName: user.NickName,
|
||
Status: user.Status,
|
||
Avatar: netutil.MergeURL(cdn, user.Avatar),
|
||
InviteCode: user.InviteCode,
|
||
LoginType: pubdef.LoginTypeAccount,
|
||
IsRegister: true,
|
||
}, nil
|
||
}
|
||
|
||
// Login 用户登录(账号均可)
|
||
func Login(ctx context.Context, account, password string, ua UAInfo) (*LoginResp, *srvdef.SrvErr) {
|
||
user, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.AccountNo: account})
|
||
if user == nil {
|
||
return nil, srvdef.New(srvdef.AccountIsNotExists, srvdef.AccountIsNotExists.Msg(), "")
|
||
}
|
||
|
||
if user.Status == pubdef.UserStatusDisable {
|
||
return nil, srvdef.New(srvdef.AccountErrAccessForbid, srvdef.AccountErrAccessForbid.Msg(), "")
|
||
}
|
||
|
||
if !checkPassword(password, user.Password) {
|
||
return nil, srvdef.New(srvdef.AccountErrPasswordError, srvdef.AccountErrPasswordError.Msg(), "")
|
||
}
|
||
|
||
token, serr := generateAndSaveToken(user.Id)
|
||
if serr != nil {
|
||
return nil, serr
|
||
}
|
||
|
||
// 更新最后登录时间、IP 及 UA(fire-and-forget)
|
||
loginSet := bson.M{
|
||
usermod.FN.User.LastLoginAt: time.Now(),
|
||
usermod.FN.User.UpdatedAt: time.Now(),
|
||
}
|
||
if ua.IP != "" {
|
||
loginSet[usermod.FN.User.LastIp] = ua.IP
|
||
}
|
||
if ua.Device != "" {
|
||
loginSet[usermod.FN.User.Device] = ua.Device
|
||
}
|
||
if ua.SysType != "" {
|
||
loginSet[usermod.FN.User.SysType] = ua.SysType.String()
|
||
}
|
||
if ua.AppVer != "" {
|
||
loginSet[usermod.FN.User.AppVer] = ua.AppVer
|
||
}
|
||
_ = usermod.UpdateOneUser(ctx,
|
||
bson.M{usermod.FN.User.Id: user.Id},
|
||
bson.M{"$set": loginSet},
|
||
)
|
||
cdn := configser.ImageCdnFromCtx(ctx)
|
||
return &LoginResp{
|
||
Token: token,
|
||
UserId: user.Id,
|
||
AccountNo: user.AccountNo,
|
||
NickName: user.NickName,
|
||
Status: user.Status,
|
||
Avatar: netutil.MergeURL(cdn, user.Avatar),
|
||
InviteCode: user.InviteCode,
|
||
LoginType: user.LoginType,
|
||
IsRegister: user.IsRegister,
|
||
Recharge: user.Recharge,
|
||
}, nil
|
||
}
|
||
|
||
// UpdateProfileReq 修改用户资料的参数(所有字段均为可选)
|
||
type UpdateProfileReq struct {
|
||
AccountNo string // 用户账号(用于定位用户,必传)
|
||
Avatar *string // 新头像 URL(可选)
|
||
NikeName *string // 昵称
|
||
OldPassword *string // 旧密码明文(修改密码时必传)
|
||
NewPassword *string // 新密码明文(修改密码时必传)
|
||
}
|
||
|
||
// UpdateProfile 修改用户资料:支持修改头像、密码,或两者同时修改
|
||
func UpdateProfile(ctx context.Context, req *UpdateProfileReq) *srvdef.SrvErr {
|
||
user, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.AccountNo: req.AccountNo})
|
||
if user == nil {
|
||
return srvdef.New(srvdef.ErrUserNotExsit, srvdef.ErrUserNotExsit.Msg(), "")
|
||
}
|
||
|
||
setDoc := bson.M{usermod.FN.User.UpdatedAt: time.Now()}
|
||
|
||
// 修改头像
|
||
if req.Avatar != nil && *req.Avatar != "" {
|
||
setDoc[usermod.FN.User.Avatar] = *req.Avatar
|
||
}
|
||
if req.NikeName != nil && *req.NikeName != "" {
|
||
setDoc[usermod.FN.User.NickName] = *req.NikeName
|
||
}
|
||
|
||
// 修改密码:两者必须同时提供
|
||
if req.OldPassword != nil || req.NewPassword != nil {
|
||
if req.OldPassword == nil || req.NewPassword == nil {
|
||
return srvdef.New(srvdef.ErrParamError, "修改密码时旧密码和新密码均需提供", "")
|
||
}
|
||
if !checkPassword(*req.OldPassword, user.Password) {
|
||
return srvdef.New(srvdef.AccountErrPasswordError, "旧密码错误", "")
|
||
}
|
||
hashed, err := hashPassword(*req.NewPassword)
|
||
if err != nil {
|
||
return srvdef.New(srvdef.Failure, "密码处理失败", "")
|
||
}
|
||
setDoc[usermod.FN.User.Password] = hashed
|
||
}
|
||
|
||
// 没有任何需要更新的字段
|
||
if len(setDoc) == 1 {
|
||
return srvdef.New(srvdef.ErrParamError, "至少需要修改一项资料", "")
|
||
}
|
||
|
||
return usermod.UpdateOneUser(ctx,
|
||
bson.M{usermod.FN.User.Id: user.Id},
|
||
bson.M{"$set": setDoc},
|
||
)
|
||
}
|
||
|
||
func getName() string {
|
||
const limit = 300
|
||
totalCnt, err := redis.SCard(redis.NameSource)
|
||
if err != nil || totalCnt < limit {
|
||
if len(appg.Name.FamilyNames) == 0 || len(appg.Name.AfterNames) == 0 {
|
||
// fmt.Println(totalCnt, err, appg.Name.FamilyNames)
|
||
return "游客" + strconv.FormatInt(time.Now().Unix(), 10)
|
||
}
|
||
for i := 0; i < limit/100; i++ {
|
||
go func() {
|
||
var names []string
|
||
for j := 0; j < limit/3; j++ {
|
||
length := 2 + rand.Intn(2)
|
||
if length == 2 {
|
||
name := appg.Name.FamilyNames[utils.RandInt(0, len(appg.Name.FamilyNames))] +
|
||
appg.Name.AfterNames[utils.RandInt(0, len(appg.Name.AfterNames))]
|
||
names = append(names, name)
|
||
|
||
}
|
||
if length == 3 {
|
||
name := appg.Name.FamilyNames[utils.RandInt(0, len(appg.Name.FamilyNames))] +
|
||
appg.Name.MiddleNames[utils.RandInt(0, len(appg.Name.MiddleNames))] +
|
||
appg.Name.AfterNames[utils.RandInt(0, len(appg.Name.AfterNames))]
|
||
names = append(names, name)
|
||
}
|
||
}
|
||
_ = redis.SAdd(redis.NameSource, names)
|
||
}()
|
||
time.Sleep(100 * time.Millisecond)
|
||
}
|
||
_ = redis.Expire(redis.NameSource, redis.NameSourceExpire)
|
||
}
|
||
|
||
name, err := redis.SPop(redis.NameSource)
|
||
if name == "" || err != nil {
|
||
return "游客" + strconv.FormatInt(time.Now().Unix(), 10)
|
||
}
|
||
return name
|
||
}
|