74 lines
3.0 KiB
Go
74 lines
3.0 KiB
Go
package auth
|
||
|
||
import (
|
||
"context"
|
||
"log/slog"
|
||
"strings"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/logx"
|
||
authdomain "hyapp/services/user-service/internal/domain/auth"
|
||
)
|
||
|
||
// recordUserDevice 在认证成功后合并当前设备档案。
|
||
// 该写入是 best-effort:device_id 为空(老客户端/后台调用)直接跳过;写失败只记日志,
|
||
// 绝不让已经成功的登录/注册/refresh 因为设备画像落库失败而回吐错误。
|
||
func (s *Service) recordUserDevice(ctx context.Context, meta Meta, userID int64, deviceID string) {
|
||
deviceID = strings.TrimSpace(deviceID)
|
||
if s.authRepository == nil || userID <= 0 || deviceID == "" {
|
||
return
|
||
}
|
||
|
||
nowMs := s.now().UnixMilli()
|
||
device := authdomain.UserDevice{
|
||
AppCode: appcode.Normalize(meta.AppCode),
|
||
UserID: userID,
|
||
DeviceID: truncateDeviceField(deviceID, 128),
|
||
Platform: truncateDeviceField(normalizePlatform(meta.Platform), 32),
|
||
DeviceModel: truncateDeviceField(meta.DeviceModel, 128),
|
||
Manufacturer: truncateDeviceField(meta.DeviceManufacturer, 64),
|
||
OSVersion: truncateDeviceField(meta.OSVersion, 64),
|
||
IMEI: truncateDeviceField(meta.IMEI, 64),
|
||
AppVersion: normalizeAuditClientVersion(meta.AppVersion),
|
||
BuildNumber: normalizeAuditClientVersion(meta.BuildNumber),
|
||
// first_seen 只在 INSERT 生效;ON DUPLICATE KEY 更新时只有 last_seen/updated 前移。
|
||
FirstSeenAtMs: nowMs,
|
||
LastSeenAtMs: nowMs,
|
||
CreatedAtMs: nowMs,
|
||
UpdatedAtMs: nowMs,
|
||
}
|
||
if err := s.authRepository.UpsertUserDevice(ctx, device); err != nil {
|
||
// 失败必须可见:设备 tab 静默缺数据比登录多一条错误日志更难排查。
|
||
logx.Error(
|
||
logx.With(ctx, slog.String("request_id", meta.RequestID), slog.String("app_code", device.AppCode)),
|
||
"user_device_upsert_failed",
|
||
err,
|
||
slog.String("component", "user_auth"),
|
||
slog.Int64("user_id", userID),
|
||
slog.String("device_id", device.DeviceID),
|
||
)
|
||
}
|
||
}
|
||
|
||
// deviceMetaWithRegistration 用注册 body 的设备字段补齐 Meta 缺口。
|
||
// 设备资料有两个来源:gateway 设备头(新客户端全局注入)和注册 body(旧协议字段);
|
||
// 任意一侧可能单独缺失,落档案前统一取“先 header 后 body”的非空值。
|
||
func deviceMetaWithRegistration(meta Meta, registration authdomain.ThirdPartyRegistration) Meta {
|
||
meta.Platform = firstNonEmpty(meta.Platform, registration.Platform)
|
||
meta.DeviceModel = firstNonEmpty(meta.DeviceModel, registration.Device)
|
||
meta.OSVersion = firstNonEmpty(meta.OSVersion, registration.OSVersion)
|
||
meta.AppVersion = firstNonEmpty(meta.AppVersion, registration.AppVersion)
|
||
meta.BuildNumber = firstNonEmpty(meta.BuildNumber, registration.BuildNumber)
|
||
return meta
|
||
}
|
||
|
||
// truncateDeviceField 按列宽截断客户端可控字符串,超长上报不能让整条设备档案 INSERT 失败。
|
||
func truncateDeviceField(value string, maxRunes int) string {
|
||
value = strings.TrimSpace(value)
|
||
runes := []rune(value)
|
||
if len(runes) <= maxRunes {
|
||
return value
|
||
}
|
||
return string(runes[:maxRunes])
|
||
}
|