同步 web_hga 的五项改造
- 移除 买点 (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>
This commit is contained in:
+69
-138
@@ -3,13 +3,12 @@ package authser
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/app/appg"
|
||||
"videoweb/app/middleware/reptilerecord"
|
||||
"videoweb/app/middleware/visit"
|
||||
"videoweb/app/service/configser"
|
||||
"videoweb/app/service/reportser"
|
||||
"videoweb/pub/common/enum/redisconst"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/netutil"
|
||||
"videoweb/pub/common/srvdef"
|
||||
@@ -17,76 +16,73 @@ import (
|
||||
"videoweb/pub/models/usermod"
|
||||
"videoweb/pub/mongos"
|
||||
"videoweb/pub/pubdef"
|
||||
"videoweb/pub/redis"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
)
|
||||
|
||||
// GuestLoginReq 游客登录请求
|
||||
type GuestLoginReq struct {
|
||||
DevID string `json:"devID"` // 设备唯一标示(必填)
|
||||
Mac string `json:"mac"` // 设备 Mac(可选)
|
||||
DeviceId string `json:"deviceId"` // 备用设备ID(可选,写入 DevV2)
|
||||
Channel string `json:"channel"` // 渠道码(可选,用户无商区码时写入 DistrictCode)
|
||||
}
|
||||
const (
|
||||
deviceUserLockRetry = 10
|
||||
deviceUserLockInterval = 100 * time.Millisecond
|
||||
)
|
||||
|
||||
// isGuestUser 是否为可升级的游客身份
|
||||
func isGuestUser(user *usermod.User) bool {
|
||||
if user == nil {
|
||||
return false
|
||||
// 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不能为空", "")
|
||||
}
|
||||
if user.Type == pubdef.UserTypeNormal {
|
||||
return false
|
||||
}
|
||||
return !user.IsRegister && strings.TrimSpace(user.AccountNo) == ""
|
||||
}
|
||||
ua.DevID = devID
|
||||
|
||||
// GuestLogin 游客登录:按 token / DevID / Mac / DeviceId 查找,不存在则创建游客
|
||||
func GuestLogin(ctx context.Context, userId int64, ip string, req *GuestLoginReq, ua useragent.UA) (*LoginResp, *srvdef.SrvErr) {
|
||||
if isBotRequest(ctx) {
|
||||
return crawlerGuestLogin()
|
||||
}
|
||||
if req == nil || strings.TrimSpace(req.DevID) == "" {
|
||||
return nil, srvdef.New(srvdef.ErrParamError, "devID不能为空", "")
|
||||
}
|
||||
ua.DevID = strings.TrimSpace(req.DevID)
|
||||
|
||||
var user *usermod.User
|
||||
var serr *srvdef.SrvErr
|
||||
reuseToken := false
|
||||
|
||||
if userId > 0 {
|
||||
user, serr = usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: userId})
|
||||
if serr != nil {
|
||||
if serr.Code != srvdef.ErrUserNotExsit {
|
||||
return nil, serr
|
||||
}
|
||||
userId = 0
|
||||
user = nil
|
||||
} else {
|
||||
reuseToken = true
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
isNewUser := false
|
||||
if userId == 0 {
|
||||
user, serr = findGuestUser(ctx, req)
|
||||
if serr != nil {
|
||||
if serr.Code != srvdef.ErrUserNotExsit {
|
||||
return nil, serr
|
||||
}
|
||||
user, serr = createGuestUser(ctx, ua, req, ip)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
isNewUser = true
|
||||
}
|
||||
user, serr := findOrCreateDeviceUser(ctx, ua, ip, channel)
|
||||
if serr != nil {
|
||||
return 0, serr
|
||||
}
|
||||
|
||||
if user.Status == pubdef.UserStatusDisable {
|
||||
return nil, srvdef.New(srvdef.AccountErrAccessForbid, srvdef.AccountErrAccessForbid.Msg(), "")
|
||||
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,
|
||||
@@ -94,9 +90,6 @@ func GuestLogin(ctx context.Context, userId int64, ip string, req *GuestLoginReq
|
||||
usermod.FN.User.LastIp: ip,
|
||||
usermod.FN.User.LoginType: pubdef.LoginTypeDevice,
|
||||
}
|
||||
if v := strings.TrimSpace(ua.DevID); v != "" {
|
||||
setDoc[usermod.FN.User.DevID] = v
|
||||
}
|
||||
if v := strings.TrimSpace(ua.DevType); v != "" {
|
||||
setDoc[usermod.FN.User.Device] = v
|
||||
}
|
||||
@@ -106,74 +99,15 @@ func GuestLogin(ctx context.Context, userId int64, ip string, req *GuestLoginReq
|
||||
if v := strings.TrimSpace(ua.Ver); v != "" {
|
||||
setDoc[usermod.FN.User.AppVer] = v
|
||||
}
|
||||
if v := strings.TrimSpace(req.Mac); v != "" {
|
||||
setDoc[usermod.FN.User.Mac] = v
|
||||
}
|
||||
if v := strings.TrimSpace(req.DeviceId); v != "" {
|
||||
setDoc[usermod.FN.User.DevV2] = v
|
||||
}
|
||||
// 历史用户 Type 为空/旧值时,未注册则归为游客
|
||||
if isGuestUser(user) && user.Type != pubdef.UserTypeGuest {
|
||||
setDoc[usermod.FN.User.Type] = pubdef.UserTypeGuest
|
||||
user.Type = pubdef.UserTypeGuest
|
||||
}
|
||||
mergeUAExtFieldsToSet(setDoc, user, ua)
|
||||
applyChannelIfMissing(setDoc, user, channel)
|
||||
_ = usermod.UpdateOneUser(ctx, bson.M{usermod.FN.User.Id: user.Id}, bson.M{"$set": setDoc})
|
||||
|
||||
var token string
|
||||
if reuseToken {
|
||||
if old, err := tokener.Get(user.Id); err == nil && len(old) > 0 {
|
||||
token = string(old)
|
||||
}
|
||||
}
|
||||
if token == "" {
|
||||
token, serr = generateAndSaveToken(user.Id)
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
}
|
||||
}
|
||||
|
||||
resp := toLoginResp(ctx, user, token, true)
|
||||
resp.IsNewUser = isNewUser
|
||||
if isNewUser {
|
||||
visit.RecordDailyVisit(ctx, user.Id, ua, ip)
|
||||
reportser.ReportUserRegister(user, ua, ip, "deviceid")
|
||||
}
|
||||
reportser.ReportUserLogin(user, ua, ip, pubdef.LoginTypeDevice)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func findGuestUser(ctx context.Context, req *GuestLoginReq) (*usermod.User, *srvdef.SrvErr) {
|
||||
user, serr := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.DevID: req.DevID})
|
||||
if serr == nil {
|
||||
return user, nil
|
||||
}
|
||||
if serr.Code != srvdef.ErrUserNotExsit {
|
||||
return nil, serr
|
||||
}
|
||||
|
||||
mac := strings.TrimSpace(req.Mac)
|
||||
if mac != "" && !strings.Contains(mac, "00:00:00:00") {
|
||||
user, serr = usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Mac: mac})
|
||||
if serr == nil {
|
||||
return user, nil
|
||||
}
|
||||
if serr.Code != srvdef.ErrUserNotExsit {
|
||||
return nil, serr
|
||||
}
|
||||
}
|
||||
|
||||
deviceId := strings.TrimSpace(req.DeviceId)
|
||||
if deviceId != "" && !strings.Contains(deviceId, "00:00:00:00") {
|
||||
return usermod.FindOneUser(ctx, bson.M{usermod.FN.User.DevV2: deviceId})
|
||||
}
|
||||
return nil, srvdef.New(srvdef.ErrUserNotExsit, "", "")
|
||||
}
|
||||
|
||||
func createGuestUser(ctx context.Context, ua useragent.UA, req *GuestLoginReq, ip string) (*usermod.User, *srvdef.SrvErr) {
|
||||
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(req.Channel, "", ip, ua.SysType)
|
||||
districtInfo := resolveUserDistrict(channel, "", ip, ua.SysType)
|
||||
user := &usermod.User{
|
||||
Id: uid,
|
||||
Type: pubdef.UserTypeGuest,
|
||||
@@ -183,13 +117,11 @@ func createGuestUser(ctx context.Context, ua useragent.UA, req *GuestLoginReq, i
|
||||
NickName: fmt.Sprintf("游客%d", uid),
|
||||
Avatar: randomAvatar(),
|
||||
InviteCode: generateUniqueInviteCode(ctx),
|
||||
DistrictCode: normalizeChannel(req.Channel),
|
||||
DistrictCode: normalizeChannel(channel),
|
||||
IsDirect: districtInfo.IsDirect,
|
||||
RegIp: ip,
|
||||
LastIp: ip,
|
||||
DevID: ua.DevID,
|
||||
Mac: strings.TrimSpace(req.Mac),
|
||||
DevV2: strings.TrimSpace(req.DeviceId),
|
||||
Device: ua.DevType,
|
||||
SysType: ua.SysType,
|
||||
AppVer: ua.Ver,
|
||||
@@ -213,14 +145,13 @@ func createGuestUser(ctx context.Context, ua useragent.UA, req *GuestLoginReq, i
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func toLoginResp(ctx context.Context, user *usermod.User, token string, withCDN bool) *LoginResp {
|
||||
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{
|
||||
Token: token,
|
||||
UserId: user.Id,
|
||||
AccountNo: user.AccountNo,
|
||||
Email: user.Email,
|
||||
@@ -247,16 +178,16 @@ func normalizeChannel(channel string) string {
|
||||
return strings.TrimSpace(channel)
|
||||
}
|
||||
|
||||
func isBotRequest(ctx context.Context) bool {
|
||||
gc, ok := ctx.(*gin.Context)
|
||||
return ok && reptilerecord.IsBot(gc)
|
||||
func hasDistrictCode(code string) bool {
|
||||
return strings.TrimSpace(code) != ""
|
||||
}
|
||||
|
||||
// crawlerGuestLogin 给爬虫签发可观看的虚拟游客 token,不建用户、不写日活、不上报。
|
||||
func crawlerGuestLogin() (*LoginResp, *srvdef.SrvErr) {
|
||||
token, serr := generateCrawlerToken()
|
||||
if serr != nil {
|
||||
return nil, serr
|
||||
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
|
||||
}
|
||||
return CrawlerLoginResp(token), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user