按意图重做在 huakui 代码底: - 删除数据埋点逻辑:移除 pub/dataReport、reportser、srv_report、 eventmod、上报定时任务及注册/登录/点赞/收藏/评论/解锁/订单处的 上报调用和 reportSdkConfig 配置下发 - app 认证改为按设备ID识别用户:去掉 token 校验、Register/Login/ ResetPassword/GuestLogin 接口,新增 ResolveDeviceUser 和设备缓存 - 广告改为按 AppId 从媒资中心拉取:pub/storage 新增 QueryAdvitise, UpdateAdvitiseData 替代 UpdateAdvitiseDataFromJt - 后台登录取消谷歌二次验证,密码正确即登录 - 短剧/专题 slug 改为 10 位大小写字母+数字混合 - 本地 app.yaml/web.yaml 保留 HKDJNET_* 模板(不提交生产凭据) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
195 lines
6.0 KiB
Go
195 lines
6.0 KiB
Go
package authser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"hkdj-web-server/app/appg"
|
|
"hkdj-web-server/app/service/configser"
|
|
"hkdj-web-server/pub/common/enum/redisconst"
|
|
"hkdj-web-server/pub/common/log"
|
|
"hkdj-web-server/pub/common/netutil"
|
|
"hkdj-web-server/pub/common/srvdef"
|
|
"hkdj-web-server/pub/common/useragent"
|
|
"hkdj-web-server/pub/models/usermod"
|
|
"hkdj-web-server/pub/mongos"
|
|
"hkdj-web-server/pub/pubdef"
|
|
"hkdj-web-server/pub/redis"
|
|
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
)
|
|
|
|
const (
|
|
deviceUserLockRetry = 10
|
|
deviceUserLockInterval = 100 * time.Millisecond
|
|
)
|
|
|
|
// ResolveDeviceUser 按设备ID识别用户:先查缓存,再查库,都没有则自动创建
|
|
func ResolveDeviceUser(ctx context.Context, ua useragent.UA, ip, channel string) (int64, *srvdef.SrvErr) {
|
|
devID := strings.TrimSpace(ua.DevID)
|
|
if devID == "" {
|
|
return 0, srvdef.New(srvdef.ErrParamError, "devID不能为空", "")
|
|
}
|
|
ua.DevID = devID
|
|
|
|
cacheKey := redisconst.AppDeviceUserKey(devID)
|
|
if v, err := redis.Get(cacheKey); err == nil && v != nil {
|
|
if userId, perr := strconv.ParseInt(*v, 10, 64); perr == nil && userId > 0 {
|
|
return userId, nil
|
|
}
|
|
}
|
|
|
|
user, serr := findOrCreateDeviceUser(ctx, ua, ip, channel)
|
|
if serr != nil {
|
|
return 0, serr
|
|
}
|
|
if user.Status == pubdef.UserStatusDisable {
|
|
return 0, srvdef.New(srvdef.AccountErrAccessForbid, srvdef.AccountErrAccessForbid.Msg(), "")
|
|
}
|
|
if err := redis.Set(cacheKey, user.Id, redisconst.AppDeviceUserExpire); err != nil {
|
|
log.Warn("authser:ResolveDeviceUser cache set failed", log.Any("devID", devID), log.E(err))
|
|
}
|
|
return user.Id, nil
|
|
}
|
|
|
|
// findOrCreateDeviceUser 按设备ID查找用户,不存在时加锁创建,避免同一设备并发请求建出多个用户
|
|
func findOrCreateDeviceUser(ctx context.Context, ua useragent.UA, ip, channel string) (*usermod.User, *srvdef.SrvErr) {
|
|
lockKey := redisconst.AppDeviceUserLockKey(ua.DevID)
|
|
for i := 0; i < deviceUserLockRetry; i++ {
|
|
user, serr := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.DevID: ua.DevID})
|
|
if serr == nil {
|
|
touchDeviceUser(ctx, user, ua, ip, channel)
|
|
return user, nil
|
|
}
|
|
if serr.Code != srvdef.ErrUserNotExsit {
|
|
return nil, serr
|
|
}
|
|
|
|
ok, err := redis.Setnx_NewOK(lockKey, 1, redisconst.AppDeviceUserLockExpire)
|
|
if err != nil {
|
|
return nil, srvdef.New(srvdef.StoreLevel, "用户创建失败", "")
|
|
}
|
|
if ok {
|
|
defer func() { _ = redis.Del(lockKey) }()
|
|
return createGuestUser(ctx, ua, ip, channel)
|
|
}
|
|
time.Sleep(deviceUserLockInterval)
|
|
}
|
|
return nil, srvdef.New(srvdef.StoreLevel, "用户创建失败", "")
|
|
}
|
|
|
|
// touchDeviceUser 更新最近访问信息(缓存未命中时执行,频率受缓存时长限制)
|
|
func touchDeviceUser(ctx context.Context, user *usermod.User, ua useragent.UA, ip, channel string) {
|
|
now := time.Now()
|
|
setDoc := bson.M{
|
|
usermod.FN.User.LastLoginAt: now,
|
|
usermod.FN.User.UpdatedAt: now,
|
|
usermod.FN.User.LastIp: ip,
|
|
usermod.FN.User.LoginType: pubdef.LoginTypeDevice,
|
|
}
|
|
if v := strings.TrimSpace(ua.DevType); v != "" {
|
|
setDoc[usermod.FN.User.Device] = v
|
|
}
|
|
if v := strings.TrimSpace(ua.SysType); v != "" {
|
|
setDoc[usermod.FN.User.SysType] = v
|
|
}
|
|
if v := strings.TrimSpace(ua.Ver); v != "" {
|
|
setDoc[usermod.FN.User.AppVer] = v
|
|
}
|
|
mergeUAExtFieldsToSet(setDoc, user, ua)
|
|
applyChannelIfMissing(setDoc, user, channel)
|
|
_ = usermod.UpdateOneUser(ctx, bson.M{usermod.FN.User.Id: user.Id}, bson.M{"$set": setDoc})
|
|
}
|
|
|
|
func createGuestUser(ctx context.Context, ua useragent.UA, ip, channel string) (*usermod.User, *srvdef.SrvErr) {
|
|
now := time.Now()
|
|
uid := mongos.Lid.User.Get()
|
|
districtInfo := resolveUserDistrict(channel, "", ip, ua.SysType)
|
|
user := &usermod.User{
|
|
Id: uid,
|
|
Type: pubdef.UserTypeGuest,
|
|
Status: pubdef.UserStatusNormal,
|
|
IsRegister: false,
|
|
LoginType: pubdef.LoginTypeDevice,
|
|
NickName: fmt.Sprintf("游客%d", uid),
|
|
Avatar: randomAvatar(),
|
|
InviteCode: generateUniqueInviteCode(ctx),
|
|
DistrictCode: normalizeChannel(channel),
|
|
IsDirect: districtInfo.IsDirect,
|
|
RegIp: ip,
|
|
LastIp: ip,
|
|
DevID: ua.DevID,
|
|
Device: ua.DevType,
|
|
SysType: ua.SysType,
|
|
AppVer: ua.Ver,
|
|
OriVer: ua.Ver,
|
|
SessionID: ua.SessionID,
|
|
DeviceBrand: ua.DeviceBrand,
|
|
DeviceModel: ua.DeviceModel,
|
|
SystemName: ua.SystemName,
|
|
SystemVersion: ua.SystemVersion,
|
|
LastLoginAt: now,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
if user.NickName == "" {
|
|
user.NickName = appg.GenerateNickname()
|
|
}
|
|
if serr := usermod.InsertOneUser(ctx, user); serr != nil {
|
|
return nil, serr
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func toLoginResp(ctx context.Context, user *usermod.User, withCDN bool) *LoginResp {
|
|
avatar := user.Avatar
|
|
if withCDN {
|
|
cdn := configser.GetAppImageCdn(configser.ClientIPFromCtx(ctx))
|
|
avatar = netutil.MergeURL(cdn, user.Avatar)
|
|
}
|
|
return &LoginResp{
|
|
UserId: user.Id,
|
|
AccountNo: user.AccountNo,
|
|
Email: user.Email,
|
|
NickName: user.NickName,
|
|
Avatar: avatar,
|
|
Introduction: user.Introduction,
|
|
DevID: user.DevID,
|
|
Mac: user.Mac,
|
|
DeviceId: user.DevV2,
|
|
InviteCode: user.InviteCode,
|
|
AgentInviteCode: user.AgentInviteCode,
|
|
Invites: user.Invites,
|
|
Status: user.Status,
|
|
Type: user.Type,
|
|
LoginType: user.LoginType,
|
|
IsRegister: user.IsRegister,
|
|
PrivacyMode: user.PrivacyMode,
|
|
Score: user.Score,
|
|
Recharge: user.Recharge,
|
|
}
|
|
}
|
|
|
|
func normalizeChannel(channel string) string {
|
|
return strings.TrimSpace(channel)
|
|
}
|
|
|
|
func hasDistrictCode(code string) bool {
|
|
return strings.TrimSpace(code) != ""
|
|
}
|
|
|
|
func applyChannelIfMissing(setDoc bson.M, user *usermod.User, channel string) {
|
|
if user == nil || hasDistrictCode(user.DistrictCode) {
|
|
return
|
|
}
|
|
code := normalizeChannel(channel)
|
|
if code == "" {
|
|
return
|
|
}
|
|
setDoc[usermod.FN.User.DistrictCode] = code
|
|
districtInfo := resolveUserDistrict(channel, user.AgentInviteCode, user.LastIp, user.SysType)
|
|
setDoc[usermod.FN.User.IsDirect] = districtInfo.IsDirect
|
|
}
|