using Dpz.Core.EnumLibrary;

namespace Dpz.Core.Public.ViewModel.Response;

/// <summary>
/// 账号会话信息
/// </summary>
public class AccountSessionResponse
{
    /// <summary>
    /// 会话记录 ID
    /// </summary>
    public required string Id { get; set; }

    /// <summary>
    /// 账号
    /// </summary>
    public required string Account { get; set; }

    /// <summary>
    /// 会话 ID
    /// </summary>
    public required string SessionId { get; set; }

    /// <summary>
    /// 登录 IP 地址
    /// </summary>
    public string? IpAddress { get; set; }

    /// <summary>
    /// 客户端原始 User-Agent
    /// </summary>
    public string? UserAgent { get; set; }

    /// <summary>
    /// 设备名称
    /// </summary>
    public string? DeviceName { get; set; }

    /// <summary>
    /// 设备类型(如 mobile、desktop、tablet)
    /// </summary>
    public string? DeviceType { get; set; }

    /// <summary>
    /// 浏览器名称
    /// </summary>
    public string? BrowserName { get; set; }

    /// <summary>
    /// 浏览器版本
    /// </summary>
    public string? BrowserVersion { get; set; }

    /// <summary>
    /// 操作系统名称
    /// </summary>
    public string? OsName { get; set; }

    /// <summary>
    /// 操作系统版本
    /// </summary>
    public string? OsVersion { get; set; }

    /// <summary>
    /// 客户端平台标识
    /// </summary>
    public string? Platform { get; set; }

    /// <summary>
    /// 屏幕宽度(像素)
    /// </summary>
    public int? ScreenWidth { get; set; }

    /// <summary>
    /// 屏幕高度(像素)
    /// </summary>
    public int? ScreenHeight { get; set; }

    /// <summary>
    /// 客户端语言
    /// </summary>
    public string? Language { get; set; }

    /// <summary>
    /// 定位纬度
    /// </summary>
    public double? Latitude { get; set; }

    /// <summary>
    /// 定位经度
    /// </summary>
    public double? Longitude { get; set; }

    /// <summary>
    /// 定位精度(米)
    /// </summary>
    public double? LocationAccuracy { get; set; }

    /// <summary>
    /// 定位来源(如 GPS、IP)
    /// </summary>
    public string? LocationSource { get; set; }

    /// <summary>
    /// 所在国家
    /// </summary>
    public string? Country { get; set; }

    /// <summary>
    /// 所在省份/地区
    /// </summary>
    public string? Region { get; set; }

    /// <summary>
    /// 所在城市
    /// </summary>
    public string? City { get; set; }

    /// <summary>
    /// 时区
    /// </summary>
    public string? TimeZone { get; set; }

    /// <summary>
    /// 网络运营商(ISP)
    /// </summary>
    public string? Isp { get; set; }

    /// <summary>
    /// 定位信息更新时间
    /// </summary>
    public DateTime? LocationUpdatedTime { get; set; }

    /// <summary>
    /// 登录方式
    /// </summary>
    public LoginMethod Method { get; set; }

    /// <summary>
    /// 是否已撤销
    /// </summary>
    public bool Revoked { get; set; }

    /// <summary>
    /// 撤销时间
    /// </summary>
    public DateTime? RevokedTime { get; set; }

    /// <summary>
    /// 创建时间
    /// </summary>
    public DateTime CreateTime { get; set; }

    /// <summary>
    /// 最近活跃时间
    /// </summary>
    public DateTime LastActiveTime { get; set; }

    /// <summary>
    /// 是否为当前会话
    /// </summary>
    public bool IsCurrent { get; set; }

    /// <summary>
    /// 设备显示名称
    /// </summary>
    public string Device =>
        string.IsNullOrWhiteSpace(DeviceName) ? ToDeviceName(UserAgent) : DeviceName;

    /// <summary>
    /// 登录方式的中文名称
    /// </summary>
    public string MethodName =>
        Method switch
        {
            LoginMethod.Password => "密码登录",
            LoginMethod.Passkey => "Passkey 登录",
            _ => Method.ToString(),
        };

    /// <summary>
    /// 会话状态的中文名称(已撤销/活跃)
    /// </summary>
    public string StatusName => Revoked ? "已撤销" : "活跃";

    /// <summary>
    /// 是否处于有效(未撤销)状态
    /// </summary>
    public bool IsSuccess => !Revoked;

    private static string ToDeviceName(string? userAgent)
    {
        if (string.IsNullOrWhiteSpace(userAgent))
        {
            return "未知设备";
        }

        return userAgent.Length <= 96 ? userAgent : $"{userAgent[..96]}...";
    }
}
⚠⚠    以下内容为AI分析的结果,请根据实际情况进行判断。

这是一个基于 C# 的响应视图模型(Response ViewModel),用于向客户端返回“账号会话信息”。在用户登录后,系统会记录一条会话记录,本类定义了该记录的完整数据结构、格式化字段和状态判断逻辑。下面分部分说明:


1. 命名空间与引用

using Dpz.Core.EnumLibrary;
namespace Dpz.Core.Public.ViewModel.Response;
  • 使用了 Dpz.Core.EnumLibrary 中的 LoginMethod 枚举(表示登录方式)。
  • 命名空间表明这是一个在公开响应中使用的视图模型。

2. 类的主要用途

AccountSessionResponse 用于描述一个账号的登录会话信息,包含:

  • 会话标识与账号
  • 登录时的 IP、User-Agent、设备、浏览器、操作系统等客户端信息
  • 地理位置信息
  • 登录方式、是否被撤销、创建和活跃时间
  • 派生出来的展示字段(设备名称、登录方式中文名、状态中文名等)

典型应用场景:用户查看自己“所有登录的会话”列表,或用于实现“会话管理/吊销”功能。


3. 各部分代码详解

3.1 会话基础信息

public required string Id { get; set; }
public required string Account { get; set; }
public required string SessionId { get; set; }
  • required 是 C# 11 特性,强制在初始化时必须提供这些属性。
  • Id 表示会话记录的数据库主键。
  • Account 是登录账号(可能是用户名/邮箱等)。
  • SessionId 是实际会话令牌对应的标识。

3.2 客户端环境信息

public string? IpAddress { get; set; }
public string? UserAgent { get; set; }
public string? DeviceName { get; set; }
public string? DeviceType { get; set; }
public string? BrowserName { get; set; }
public string? BrowserVersion { get; set; }
public string? OsName { get; set; }
public string? OsVersion { get; set; }
public string? Platform { get; set; }
public int? ScreenWidth { get; set; }
public int? ScreenHeight { get; set; }
public string? Language { get; set; }
  • 所有可空类型 ? 表示该字段可缺失(可能未采集到)。
  • 包含浏览器、操作系统、平台、屏幕分辨率、语言等信息。
  • 这些数据通常由前端 JS 收集后随着登录请求发送,或从 User-Agent 解析而来。

3.3 地理位置信息

public double? Latitude { get; set; }
public double? Longitude { get; set; }
public double? LocationAccuracy { get; set; }
public string? LocationSource { get; set; }
public string? Country { get; set; }
public string? Region { get; set; }
public string? City { get; set; }
public string? TimeZone { get; set; }
public string? Isp { get; set; }
public DateTime? LocationUpdatedTime { get; set; }
  • 经纬度、精度、来源说明(如 GPS 或 IP 定位)。
  • 国家、地区、城市、时区、网络运营商(ISP)。
  • 记录定位信息最后一次更新时间,可用于时效判断。

3.4 登录与状态信息

public LoginMethod Method { get; set; }
public bool Revoked { get; set; }
public DateTime? RevokedTime { get; set; }
public DateTime CreateTime { get; set; }
public DateTime LastActiveTime { get; set; }
public bool IsCurrent { get; set; }
  • Method: 使用枚举 LoginMethod,包含密码登录、Passkey 登录等。
  • Revoked: 表示该会话是否已被用户主动撤销。
  • RevokedTime: 撤销的时间(如果已撤销)。
  • CreateTime: 会话创建时间。
  • LastActiveTime: 最近一次活跃时间,用于判断是否活跃。
  • IsCurrent: 是否为当前正在使用的会话。

4. 计算属性(只读派生属性)

4.1 设备显示名称

public string Device =>
    string.IsNullOrWhiteSpace(DeviceName) ? ToDeviceName(UserAgent) : DeviceName;
  • DeviceName 为空,则通过 ToDeviceName(UserAgent) 从 User-Agent 中生成一个展示名称。
  • 否则直接使用 DeviceName

4.2 登录方式中文名

public string MethodName =>
    Method switch
    {
        LoginMethod.Password => "密码登录",
        LoginMethod.Passkey => "Passkey 登录",
        _ => Method.ToString(),
    };
  • 根据 LoginMethod 枚举值映射为中文名称。
  • _ 是默认分支,会输出枚举原本的字符串形式。

4.3 状态中文名

public string StatusName => Revoked ? "已撤销" : "活跃";
  • 简单根据布尔值输出中文状态。

4.4 是否有效状态

public bool IsSuccess => !Revoked;
  • 取反 Revoked,用于快速判断该会话是否仍在有效状态,方便调用方做逻辑判断。

5. 私有辅助方法

private static string ToDeviceName(string? userAgent)
{
    if (string.IsNullOrWhiteSpace(userAgent))
        return "未知设备";
    return userAgent.Length <= 96 ? userAgent : $"{userAgent[..96]}...";
}
  • DeviceName 为空时,此方法将完整的 User-Agent 字符串作为设备名返回。
  • 如果 User-Agent 过长(超过 96 字符),则截取前 96 个字符并加省略号,避免 UI 展示过长。
  • 如果 User-Agent 也为空,则返回“未知设备”。

6. 整体设计思想

该类是典型的 响应模型(Response Model),集成了数据存储字段和展示派生属性。它让后端可以直接返回丰富的会话信息,前端无需再做额外处理即可渲染界面。required 和可空类型确保了数据完整性;派生属性和私有方法则进一步封装了格式化逻辑,使得调用方代码更加简洁清晰。

评论加载中...