hi-server/docs/设备移出和邀请码优化/CONSENSUS_设备移出和邀请码优化.md

3.7 KiB
Raw Blame History

设备移出和邀请码优化 - 共识文档(更新版)

需求概述

修复两个 Bug

  1. Bug 1设备B绑定邮箱后被从设备A移除设备B没有被踢下线
  2. Bug 2:输入不存在的邀请码时,提示信息不友好

Bug 1设备移出后未自动退出

根本原因

设备B绑定邮箱迁移到邮箱用户

  • 数据库更新了设备的 UserId
  • DeviceManager 内存中设备B的 WebSocket 连接仍在原用户名下
  • Redis 缓存中设备B的 session 未被清理

解绑设备B时KickDevice(用户1, "device-b") 在用户1的设备列表中找不到 device-b因为连接还在原用户名下

修复方案

文件1bindEmailWithVerificationLogic.go

在设备迁移后,踢出旧连接并清理缓存:

// 第 139-158 行之后添加
for _, device := range devices {
    device.UserId = emailUserId
    err = l.svcCtx.UserModel.UpdateDevice(l.ctx, device)
    // ...existing code...
    
    // 新增:踢出旧连接并清理缓存
    l.svcCtx.DeviceManager.KickDevice(u.Id, device.Identifier)
    
    deviceCacheKey := fmt.Sprintf("%v:%v", config.DeviceCacheKeyKey, device.Identifier)
    if sessionId, _ := l.svcCtx.Redis.Get(l.ctx, deviceCacheKey).Result(); sessionId != "" {
        sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
        _ = l.svcCtx.Redis.Del(l.ctx, deviceCacheKey).Err()
        _ = l.svcCtx.Redis.Del(l.ctx, sessionIdCacheKey).Err()
    }
}

文件2unbindDeviceLogic.go(防御性修复)

补充 user_sessions 清理逻辑,与 deleteUserDeviceLogic.go 保持一致:

// 第 118-122 行,补充 sessionsKey 清理
if sessionId, rerr := l.svcCtx.Redis.Get(ctx, deviceCacheKey).Result(); rerr == nil && sessionId != "" {
    _ = l.svcCtx.Redis.Del(ctx, deviceCacheKey).Err()
    sessionIdCacheKey := fmt.Sprintf("%v:%v", config.SessionIdKey, sessionId)
    _ = l.svcCtx.Redis.Del(ctx, sessionIdCacheKey).Err()
    // 新增:清理 user_sessions
    sessionsKey := fmt.Sprintf("%s%v", config.UserSessionsKeyPrefix, device.UserId)
    _ = l.svcCtx.Redis.ZRem(ctx, sessionsKey, sessionId).Err()
}

Bug 2邀请码错误提示不友好

根本原因

bindInviteCodeLogic.go 中未区分"邀请码不存在"和"数据库错误"。

修复方案

// 第 44-47 行修改为
referrer, err := l.svcCtx.UserModel.FindOneByReferCode(l.ctx, req.InviteCode)
if err != nil {
    if errors.Is(err, gorm.ErrRecordNotFound) {
        return errors.Wrapf(xerr.NewErrCodeMsg(xerr.InviteCodeError, "无邀请码"), "invite code not found")
    }
    logger.WithContext(l.ctx).Error(err)
    return errors.Wrapf(xerr.NewErrCode(xerr.DatabaseQueryError), "query referrer failed: %v", err.Error())
}

涉及文件汇总

文件 修改类型 优先级
internal/logic/public/user/bindEmailWithVerificationLogic.go 核心修复
internal/logic/public/user/unbindDeviceLogic.go 防御性修复
internal/logic/public/user/bindInviteCodeLogic.go Bug 修复

验收标准

Bug 1 验收

  • 设备B绑定邮箱后设备B的旧 Token 失效
  • 设备B绑定邮箱后设备B的 WebSocket 连接被断开
  • 在设备A上移除设备B后设备B立即被踢下线
  • 设备B无法继续使用旧 Token 调用 API

Bug 2 验收

  • 输入不存在的邀请码时,返回错误码 20009
  • 错误消息显示"无邀请码"

验证计划

  1. 编译验证go build ./...
  2. 手动测试
    • 设备B绑定邮箱 → 检查是否被踢下线
    • 设备A移除设备B → 检查设备B是否被踢下线
    • 输入无效邀请码 → 检查错误提示