Files
rootandClaude Opus 4.7 e7179da072 feat: 同步 huangguo2-server 的 11 个非基线提交
按意图重做在 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>
2026-10-16 12:59:14 +08:00

179 lines
6.1 KiB
Go

package authser
import (
"context"
"strings"
"time"
"hkdj-web-server/pub/common/log"
"hkdj-web-server/pub/common/srvdef"
"hkdj-web-server/pub/common/useragent"
"hkdj-web-server/pub/common/utils"
"hkdj-web-server/pub/models/usermod"
"hkdj-web-server/pub/pubdef"
"go.mongodb.org/mongo-driver/bson"
)
var defaultAvatars = []string{
"v3/image/bo/ty/1016/girl1.jpeg",
"v3/image/re/9y/1016/girl2.jpeg",
"v3/image/wr/1t/1016/girl3.jpeg",
"v3/image/8l/26/1016/girl4.jpeg",
"v3/image/22/6l/1016/girl5.jpeg",
"v3/image/8h/16/1016/girl6.jpeg",
"v3/image/wj/15/1016/girl7.jpeg",
"v3/image/t2/13/1016/girl8.jpeg",
"v3/image/4r/j/1016/girl9.jpeg",
"v3/image/16c/9/1016/girl10.jpeg",
}
func randomAvatar() string {
idx := time.Now().UnixNano() % int64(len(defaultAvatars))
return defaultAvatars[idx]
}
// generateUniqueInviteCode 生成不与已有用户冲突的 12 位邀请码(简化实现)
func generateUniqueInviteCode(ctx context.Context) string {
return strings.ToUpper(utils.GetRandomLetters(12))
}
// LoginResp 用户信息
type LoginResp struct {
UserId int64 `json:"userId"`
AccountNo string `json:"accountNo"`
Email string `json:"email"`
NickName string `json:"nickName"`
Avatar string `json:"avatar"`
Introduction string `json:"introduction"`
DevID string `json:"devID"`
Mac string `json:"mac"`
DeviceId string `json:"deviceId"`
InviteCode string `json:"inviteCode"`
AgentInviteCode string `json:"agentInviteCode"`
Invites int64 `json:"invites"`
Status pubdef.UserStatus `json:"status"`
Type pubdef.UserType `json:"type"` // 身份类型:0游客 1普通用户
LoginType pubdef.LoginType `json:"loginType"` // 登陆方式
IsRegister bool `json:"isRegister"` // 是否已注册
PrivacyMode bool `json:"privacyMode"` // 隐私模式
Score int64 `json:"score"` // 积分
Recharge int64 `json:"recharge"` // 金币余额
}
// CrawlerLoginResp 爬虫虚拟游客资料,不对应任何数据库用户。
func CrawlerLoginResp() *LoginResp {
return &LoginResp{
UserId: pubdef.CrawlerUserId,
NickName: "游客",
Status: pubdef.UserStatusNormal,
Type: pubdef.UserTypeGuest,
LoginType: pubdef.LoginTypeDevice,
IsRegister: false,
}
}
// GetInfo 获取当前用户的最新信息
func GetInfo(ctx context.Context, userId int64, ua useragent.UA) (*LoginResp, *srvdef.SrvErr) {
if pubdef.IsCrawlerUser(userId) {
return CrawlerLoginResp(), nil
}
user, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: userId})
if user == nil {
return nil, srvdef.New(srvdef.ErrUserNotExsit, srvdef.ErrUserNotExsit.Msg(), "")
}
now := time.Now()
setDoc := bson.M{}
if v := strings.TrimSpace(ua.DevID); v != "" && v != user.DevID {
setDoc[usermod.FN.User.DevID] = v
user.DevID = v
}
if v := strings.TrimSpace(ua.DevType); v != "" && v != user.Device {
setDoc[usermod.FN.User.Device] = v
user.Device = v
}
if v := strings.TrimSpace(ua.SysType); v != "" && v != user.SysType {
setDoc[usermod.FN.User.SysType] = v
user.SysType = v
}
if v := strings.TrimSpace(ua.Ver); v != "" && v != user.AppVer {
setDoc[usermod.FN.User.AppVer] = v
user.AppVer = v
}
mergeUAExtFieldsToSet(setDoc, user, ua)
if len(setDoc) > 0 {
setDoc[usermod.FN.User.UpdatedAt] = now
_ = usermod.UpdateOneUser(ctx, bson.M{usermod.FN.User.Id: user.Id}, bson.M{"$set": setDoc})
}
return toLoginResp(ctx, user, true), nil
}
func mergeUAExtFieldsToSet(setDoc bson.M, user *usermod.User, ua useragent.UA) {
if v := strings.TrimSpace(ua.SessionID); v != "" && v != user.SessionID {
setDoc[usermod.FN.User.SessionID] = v
}
if v := strings.TrimSpace(ua.DeviceBrand); v != "" && v != user.DeviceBrand {
setDoc[usermod.FN.User.DeviceBrand] = v
}
if v := strings.TrimSpace(ua.DeviceModel); v != "" && v != user.DeviceModel {
setDoc[usermod.FN.User.DeviceModel] = v
}
if v := strings.TrimSpace(ua.SystemName); v != "" && v != user.SystemName {
setDoc[usermod.FN.User.SystemName] = v
}
if v := strings.TrimSpace(ua.SystemVersion); v != "" && v != user.SystemVersion {
setDoc[usermod.FN.User.SystemVersion] = v
}
if v := strings.TrimSpace(ua.Ver); v != "" && user.OriVer == "" {
setDoc[usermod.FN.User.OriVer] = v
}
}
// UpdateProfileReq 修改用户资料的参数(所有字段均为可选)
type UpdateProfileReq struct {
UserId int64 // 用户ID(按设备ID识别)
NickName *string // 新昵称(可选)
Avatar *string // 新头像 URL(可选)
Introduction *string // 简介(可选)
PrivacyMode *bool // 隐私模式:true-开启,false-关闭(可选)
}
// UpdateProfile 修改用户资料:支持修改昵称、头像、简介、隐私模式,或组合修改
func UpdateProfile(ctx context.Context, req *UpdateProfileReq) *srvdef.SrvErr {
user, _ := usermod.FindOneUser(ctx, bson.M{usermod.FN.User.Id: req.UserId})
if user == nil {
return srvdef.New(srvdef.ErrUserNotExsit, srvdef.ErrUserNotExsit.Msg(), "")
}
setDoc := bson.M{usermod.FN.User.UpdatedAt: time.Now()}
if req.NickName != nil {
nick := strings.TrimSpace(*req.NickName)
if nick == "" {
return srvdef.New(srvdef.ErrParamError, "昵称不能为空", "")
}
setDoc[usermod.FN.User.NickName] = nick
}
if req.Avatar != nil {
setDoc[usermod.FN.User.Avatar] = strings.TrimSpace(*req.Avatar)
}
if req.Introduction != nil {
setDoc[usermod.FN.User.Introduction] = strings.TrimSpace(*req.Introduction)
}
if req.PrivacyMode != nil {
setDoc[usermod.FN.User.PrivacyMode] = *req.PrivacyMode
}
// 没有任何需要更新的字段
if len(setDoc) == 1 {
return srvdef.New(srvdef.ErrParamError, "至少需要修改一项资料", "")
}
if serr := usermod.UpdateOneUser(ctx,
bson.M{usermod.FN.User.Id: user.Id},
bson.M{"$set": setDoc},
); serr != nil {
log.Error("UpdateProfile UpdateOneUser failed", log.Any("userId", user.Id), log.E(serr))
return serr
}
return nil
}