|
|
// ============================================================================
|
|
|
// 【文件说明】HwPortalContextHelper.cs - 上下文帮助类
|
|
|
// ============================================================================
|
|
|
// 这个类提供一些"获取当前请求上下文信息"的静态方法。
|
|
|
// 例如:当前时间、当前用户名、当前请求 IP 等。
|
|
|
//
|
|
|
// 在 Java Spring Boot 中,你可能会这样做:
|
|
|
// - 获取用户:SecurityContextHolder.getContext().getAuthentication().getName()
|
|
|
// - 获取 IP:request.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 获取 IP:request.getRemoteAddr()
|
|
|
// 但 Java 返回的是字符串,不需要手动转换格式。
|
|
|
return httpContext?.Connection?.RemoteIpAddress?.MapToIPv4().ToString();
|
|
|
}
|
|
|
}
|