- 移除 买点 (user acquisition tracking) 子系统:dataReport SDK、eventmod 事件模型、reportdata 定时上报 job、reportser/srv_report 包装层,以及业务 侧的 ~20 处 Report* 调用点;去掉 proto.ConfigInfo 的 ReportSDKConfig 字段、DataReport yaml 配置块和 pubdef.TrackerAppId。 - 广告获取改走 versionUrl /api/stat/advitise/v2/get(appId=12),移除 adcenter(Jt) 调用;新增 POST /api/app/ads/click 异步上报; proto.AdvitiseData 别名 proxy.AdvitiseData,AList/BList 改名 AppInfoAList/AppInfoBList。 - web 后台登录取消谷歌验证码(srv_mwacc.AccLogin 去掉 code 参数),密码 正确即发放 token。 - app 端取消注册/登录/找回密码/游客登录接口及密码、token 相关代码;鉴权 中间件按 X-User-Agent 中的 devID 识别用户,首次访问自动建号,Redis 缓存 10 分钟;修改资料不再支持改密码。 - app m3u8 ticket 校验失败不再降级到 fallback 路径,仍按原 m3u8Name 返回。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
194 lines
6.0 KiB
Go
194 lines
6.0 KiB
Go
package authser
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
"videoweb/app/appg"
|
|
"videoweb/app/service/configser"
|
|
"videoweb/pub/common/enum/redisconst"
|
|
"videoweb/pub/common/log"
|
|
"videoweb/pub/common/netutil"
|
|
"videoweb/pub/common/srvdef"
|
|
"videoweb/pub/common/useragent"
|
|
"videoweb/pub/models/usermod"
|
|
"videoweb/pub/mongos"
|
|
"videoweb/pub/pubdef"
|
|
"videoweb/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,
|
|
LoginType: pubdef.LoginTypeDevice,
|
|
IsRegister: false,
|
|
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 nick := appg.GenerateNickname(); nick != "" {
|
|
user.NickName = nick
|
|
}
|
|
if serr := usermod.InsertOneUser(ctx, user); serr != nil {
|
|
log.Error("authser:createGuestUser InsertOneUser failed", log.E(serr), log.Any("devID", ua.DevID))
|
|
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,
|
|
Introduction: user.Introduction,
|
|
VipType: user.VipType,
|
|
CurrentVipExpire: user.CurrentVipExpire,
|
|
ShowCardId: user.ShowCardId,
|
|
ShowCardName: user.ShowCardName,
|
|
Status: user.Status,
|
|
Avatar: avatar,
|
|
InviteCode: user.InviteCode,
|
|
Type: user.Type,
|
|
LoginType: user.LoginType,
|
|
IsRegister: user.IsRegister,
|
|
PrivacyMode: user.PrivacyMode,
|
|
HasPassword: strings.TrimSpace(user.Password) != "",
|
|
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
|
|
}
|
|
if ch := normalizeChannel(channel); ch != "" {
|
|
setDoc[usermod.FN.User.DistrictCode] = ch
|
|
user.DistrictCode = ch
|
|
}
|
|
}
|