You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
5.5 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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