feat(worker): add device registration_id cleanup worker
Add DeviceCleanupWorker to periodically clean up stale device tokens that haven't been used within the retention period. This prevents accumulation of invalid registration_ids and maintains data hygiene. - New DeviceCleanupConfig with enabled, retention_days (3), and interval_minutes (360) - New repository methods DeleteStaleDevices and DeleteOldestActiveDevice - Updated RegisterDevice to evict oldest active device when reaching limit - Reduced MaxDevicesPerUser from 10 to 3 per user - Worker integrated into app startup/shutdown lifecycle
This commit is contained in:
@@ -28,7 +28,7 @@ const (
|
||||
// PushQueueSize 推送队列大小
|
||||
PushQueueSize = 1000
|
||||
// MaxDevicesPerUser 每个用户最大设备数
|
||||
MaxDevicesPerUser = 10
|
||||
MaxDevicesPerUser = 3
|
||||
)
|
||||
|
||||
// ChatMessageSender 聊天消息发送者信息
|
||||
@@ -386,12 +386,9 @@ func (s *pushServiceImpl) pushViaJPushBatch(devices []*model.DeviceToken, params
|
||||
// 处理同设备多账号场景:registration_id(pushToken)是设备级标识,
|
||||
// 同一物理设备登录新账号时 pushToken 不变,需先失效其他用户对该 pushToken 的关联,
|
||||
// 避免给一个账号推送时同设备的其他账号也收到通知。
|
||||
// 每个用户最多保留 MaxDevicesPerUser 个活跃设备:注册后统一收敛到限额以内,
|
||||
// 既能处理达上限淘汰,也能收敛历史超额数据(如限额从 10 调整为 3 后的存量清理)。
|
||||
func (s *pushServiceImpl) RegisterDevice(ctx context.Context, userID string, deviceID string, deviceType model.DeviceType, pushToken string) error {
|
||||
existing, err := s.deviceRepo.GetByUserID(userID)
|
||||
if err == nil && len(existing) >= MaxDevicesPerUser {
|
||||
return fmt.Errorf("exceeded maximum device limit (%d)", MaxDevicesPerUser)
|
||||
}
|
||||
|
||||
// 失效其他用户/设备对同一 pushToken 的关联(防止跨账号误推)
|
||||
if pushToken != "" {
|
||||
if dErr := s.deviceRepo.DeactivateByPushToken(pushToken); dErr != nil {
|
||||
@@ -413,7 +410,28 @@ func (s *pushServiceImpl) RegisterDevice(ctx context.Context, userID string, dev
|
||||
}
|
||||
deviceToken.UpdateLastUsed()
|
||||
|
||||
return s.deviceRepo.Upsert(deviceToken)
|
||||
if err := s.deviceRepo.Upsert(deviceToken); err != nil {
|
||||
return fmt.Errorf("failed to upsert device: %w", err)
|
||||
}
|
||||
|
||||
// 收敛活跃设备数到 MaxDevicesPerUser 以内(保留最近使用的,淘汰超额的最旧设备)
|
||||
// 覆盖两种场景:① 新设备注册导致超限;② 历史存量超额(限额调整后逐步收敛)
|
||||
if evicted, err := s.deviceRepo.TrimActiveDevices(userID, MaxDevicesPerUser); err != nil {
|
||||
zap.L().Warn("trim active devices failed",
|
||||
zap.String("userID", userID),
|
||||
zap.Int("limit", MaxDevicesPerUser),
|
||||
zap.Error(err),
|
||||
)
|
||||
} else if evicted > 0 {
|
||||
zap.L().Info("evicted oldest devices for device limit",
|
||||
zap.String("userID", userID),
|
||||
zap.String("deviceID", deviceID),
|
||||
zap.Int64("evicted", evicted),
|
||||
zap.Int("limit", MaxDevicesPerUser),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnregisterDevice 注销设备
|
||||
|
||||
Reference in New Issue
Block a user