// ============================================================================ // 【文件说明】HwPortalContextHelper.cs - 上下文帮助类 // ============================================================================ // 这个类提供一些"获取当前请求上下文信息"的静态方法。 // 例如:当前时间、当前用户名、当前请求 IP 等。 // // 在 Java Spring Boot 中,你可能会这样做: // - 获取用户:SecurityContextHolder.getContext().getAuthentication().getName() // - 获取 IP:request.getRemoteAddr() // // 在 ASP.NET Core + Furion 中,方式略有不同,这个类封装了这些差异。 // ============================================================================ namespace Admin.NET.Plugin.HwPortal; /// /// 门户上下文帮助类。 /// /// 【C# 语法知识点 - internal static class】 /// internal:表示"程序集内部可见",只能被当前项目(.dll)内的代码访问。 /// static:表示"静态类",不能被实例化,所有成员必须是静态的。 /// /// 为什么用 internal static? /// - 这个类只是内部工具方法,不需要暴露给外部调用者 /// - 静态类可以直接用类名调用方法,不需要 new 对象 /// /// 对比 Java: /// Java 没有 internal 关键字,通常用 package-private(不写访问修饰符)来限制可见性。 /// Java 的静态类通常用 private 构造函数 + 静态方法来模拟。 /// /// internal static class HwPortalContextHelper { /// /// 获取当前时间。 /// /// 【为什么封装这个方法?】 /// 看起来多此一举,直接 DateTime.Now 不就行了? /// 但在单元测试时,这个封装就很有用: /// - 你可以用 Mock 框架替换这个方法返回固定时间 /// - 测试"创建时间是否正确"时,不需要真的等待时间流逝 /// /// 这叫"时间抽象",是可测试性设计的一部分。 /// /// /// 当前本地时间 public static DateTime Now() { // DateTime.Now 返回本地时间,DateTime.UtcNow 返回 UTC 时间。 // 在服务器开发中,推荐用 UTC 时间存储,显示时再转本地时间。 // 这里保持用本地时间是为了兼容原 Java 代码的行为。 return DateTime.Now; } /// /// 获取当前用户名。 /// /// 【C# 语法知识点 - ?. 空条件运算符】 /// App.User?.FindFirst(...) 中的 ?. 是"空条件运算符"。 /// 含义:如果 App.User 不为 null,才调用后面的方法;否则直接返回 null。 /// /// 对比 Java: /// Java 需要这样写: /// if (App.User != null) { /// Claim claim = App.User.FindFirst(...); /// if (claim != null) { /// return claim.getValue(); /// } /// } /// /// C# 可以一行搞定: /// App.User?.FindFirst(ClaimConst.Account)?.Value /// /// 这就是 C# 的"空值传播"语法,大幅减少空指针检查代码。 /// /// /// 【C# 语法知识点 - ?? 空合并运算符】 /// ?? 是"空合并运算符":左边不为 null 就返回左边,否则返回右边。 /// /// 这行代码的完整逻辑是: /// 1. 先尝试从 Account 声明获取用户名 /// 2. 如果没有,尝试从 Identity.Name 获取 /// 3. 如果都没有,返回默认值 "system" /// /// 对比 Java: /// Java 需要多个 if-else 或三元运算符: /// String name = account != null ? account : (identityName != null ? identityName : "system"); /// /// /// 当前登录用户名,未登录则返回 "system" public static string CurrentUserName() { // App.User 是 Furion 框架提供的静态属性,代表当前登录用户的 ClaimsPrincipal。 // ClaimsPrincipal 类似 Java Spring Security 的 Authentication 对象。 // // ClaimConst.Account 是自定义的声明类型常量,代表"账号名"声明。 // FindFirst() 方法查找指定类型的声明,返回 Claim 对象。 // .Value 获取声明的值。 return App.User?.FindFirst(ClaimConst.Account)?.Value ?? App.User?.Identity?.Name ?? "system"; } /// /// 获取当前请求的客户端 IP 地址。 /// /// HTTP 上下文对象 /// IPv4 地址字符串,获取失败返回 null public static string CurrentRequestIp(HttpContext httpContext) { // 【获取 IP 的完整链路】 // httpContext.Connection:获取连接信息对象 // .RemoteIpAddress:获取远程 IP 地址(可能是 IPv6) // ?.MapToIPv4():将 IPv6 映射到 IPv4 格式(如果原来是 IPv4 则不变) // ?.ToString():转换为字符串形式,如 "192.168.1.100" // // 为什么用 MapToIPv4()? // 现代服务器可能监听 IPv6,客户端连接时 RemoteIpAddress 可能是 IPv6 格式。 // 前端和日志系统通常期望 IPv4,所以这里做个转换。 // // 对比 Java Spring Boot: // Java 获取 IP:request.getRemoteAddr() // 但 Java 返回的是字符串,不需要手动转换格式。 return httpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString(); } }